Repository navigation
The experiments of a run run in a pool of worker processes - #387
Merged
Merged
Conversation
The experiments of a run are independent of each other. run_experiments (n_workers=None) starts a worker per four experiments, at most one per CPU, and runs a few experiments serially. A worker creates an experiment again from its class and the settings of the runner, keeps the loaded models for its next experiment, runs it with the parameters of matplotlib of the run and sends back its result and its log records, which the parent logs in the order of the experiments. The most expensive experiments start first, the forkserver preloads the modules of the experiments, and an experiment whose worker died runs in the parent again. A run which shows its figures, keeps its results, has a unit registry of its own or whose experiments cannot be pickled is serial. The 28 experiments of the HCTZ model of pkdb_models take 12 s instead of 21 s.
- A worker logs the messages of an experiment through the run only: a handler of the worker, e.g. of a script which configures logging when it is imported, printed them a second time and out of order. - An experiment of the main script runs in the calling process: a class defined behind the guard of the script pickles but a worker, which imports the script without its guard, does not find it, and the pool broke. - By default a runner whose simulator runs the scans in a pool runs its experiments in the calling process, so the scans keep their pool. - The results of an experiment of a pool are released as the ones of a serial run, and a message which cannot be pickled is sent as text.
matthiaskoenig
force-pushed
the
parallel-experiments
branch
from
October 24, 2026 06:42
b0a2a29 to
8080ad3
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
The experiments of a run are independent, but
ExperimentRunner.run_experimentsran them one after the other. Most of the time of a run goes into drawing figures, which used a single CPU.What
run_experiments(n_workers=None), also on the module functionrun_experiments, runs the experiments in a pool started withparallel.start_pool.EXPERIMENTS_PER_WORKER(4) experiments, at most one per CPU.n_workers=1is serial; a number gives a pool of that many workers, at most one per experiment.What a worker gets and does. A worker (
_run_job) receives a pickled_Job:_changed_rc_params, e.g. therc_contextof pkpdsim) and the levels of the loggers.The worker then:
parallel.worker_cachefor its next experiment, and runs the experiment with a simulator of one worker;ExperimentResultand its log records (_Outcome).What the parent does.
_cost: the points of the scans andFIGURE_COSTper figure).BrokenProcessPool) runs in the parent again.Serial runs. The run stays serial, with a log message giving the reason (
_serial_reason,_scan_pool), when:Simulator(n_workers>1), orNonewith a scan ofPOOL_THRESHOLDpoints), so the scans keep that pool. An explicitn_workerspools the experiments anyway.Logging. While an experiment runs, the worker's root logger has a single handler,
_Collect. A handler the worker set up itself, e.g. one installed by a script that configures logging when it is imported, would print every message a second time and out of order. A record that cannot be pickled is sent as plain text.The parent marks the results of a pooled experiment as released, as in a serial run.
Measurements
run_simulation pkdb_models.models.hctz --action simulate --experiments all(28 experiments, python 3.15, 20 CPUs):The default for 28 experiments is 7 workers. The 740 SVG, TSV, JSON and HTML files of the run with 8 workers are the same as those of the serial run.
Trade-off
An experiment runs its scans serially in its worker. The most expensive experiment of the HCTZ model, a parameter scan, takes 5.6 s in a worker and is most of the remaining wall time. A run of a single large scan is faster with
n_workers=1, which leaves the scan to the pool of the simulator. A script which runs a pool needs the guardif __name__ == "__main__":, as for a pooled scan.Tests
tests/experiment/test_runner_parallel.py, ten tests:n_workers=0raises;The review also found that the duplicated logs, the broken pool for a class behind the guard, and the lost scan pool under
pkpdsim --workers Ncould happen; all three are fixed in the second commit.pytest --skip-slow tests/experiment tests/report tests/examples tests/plot tests/test_parallel.py: 483 passed.