Make multiprocessing calls aware of parallelism used during testing#1404
Conversation
Pytest seems to read the conf file at a different time during execution if it is located at the top of the repo versus in a subdirectory. Moving it the top makes things work more predictably.
Thanks to Gemini 3.5 Flash for help.
There was a problem hiding this comment.
Code Review
This pull request introduces a new utility function get_available_cpu_count to dynamically determine available CPU cores while respecting process affinity, Docker limits, and pytest-xdist workers. It replaces standard CPU count calls across the codebase and adds a pytest fixture to limit thread pools. The review feedback correctly identifies a double-division bug in conftest.py where the CPU count is divided by the worker count twice, and provides the necessary test updates to accompany the fix.
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
As pointed out by Gemini Code Assist, the cpu count was already divided by the xdist worker count.
|
/gemini review |
There was a problem hiding this comment.
Code Review
This pull request introduces a new utility function get_available_cpu_count to safely determine the number of available CPU cores while respecting process affinity, Docker limits, and pytest-xdist worker counts. This utility is integrated across the codebase to replace standard multiprocessing.cpu_count() and os.cpu_count() calls, and a new pytest fixture is added to limit thread pools during parallel testing. Feedback on these changes highlights two main issues: first, setting MKL_NUM_THREADS in thc_factorization.py and thc_objectives.py is ineffective because get_available_cpu_count transitively imports numpy and scipy before the environment variable is set; second, using ParallelLinearQubitOperator when only one process is available introduces unnecessary multiprocessing overhead, suggesting a fallback to sequential execution.
The fact that operator_utils.py imported numpy, scipy, and other libraries made it impossible to import `get_available_cpu_count()` alone. This in turn prevented using it to set things before NumPy and other libraries were loaded.
|
/gemini review |
There was a problem hiding this comment.
Code Review
This pull request introduces a helper function get_available_cpu_count to safely determine the number of available CPU cores, respecting process affinity, Docker limits, and pytest-xdist workers. This helper is integrated across several modules, including linear_qubit_operator and THC resource estimation utilities, to replace standard multiprocessing.cpu_count() or os.cpu_count() calls. Additionally, a conftest.py fixture is added to limit threadpools when running parallel tests. The reviewer's feedback suggests avoiding unconditionally overwriting the MKL_NUM_THREADS environment variable in thc_factorization.py and thc_objectives.py to prevent overriding explicit user configurations in shared or HPC environments.
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
|
/gemini review |
There was a problem hiding this comment.
Code Review
This pull request introduces a robust get_available_cpu_count utility to accurately determine available CPU cores while respecting process affinity, Docker container limits, and pytest-xdist worker counts. It integrates this utility across the codebase, replacing standard multiprocessing.cpu_count and os.cpu_count calls, and adds a pytest fixture to limit threadpools during parallel testing. The review feedback suggests extending the environment variable configuration in the THC utilities to also set OMP_NUM_THREADS and OPENBLAS_NUM_THREADS alongside MKL_NUM_THREADS to ensure comprehensive thread limiting across different BLAS backends.
|
Warning Gemini encountered an error creating the review. You can try again by commenting |
This was inconsistent with check/pylint.
Adjusted a test per review comment from Adam R.
Per review comment by Adam.
arettig
left a comment
There was a problem hiding this comment.
Looks good! I can confirm it is still very fast on my machine.
A couple small questions about defaulting to parallel vs. serial below. From what I can tell pytest and mypy run in serial by default whereas pylint, and format-incremental run in parallel by default. (Of course this largely doesn't matter for anything except the pytests).
|
I thought that |
|
Regarding format-incremental: it uses It's tempting to also change it, to make it consistent (i.e., make it run serially by default), but because it doesn't run any code (unlike pytest), and because I'm not sure what benefit there would be, I'm leaning towards leaving it as-is (parallel). |
Pytest on parallel systems was taking far more time to run than it should have. The problem turned out to be due to the fact that some of the OpenFermion code used multiprocessing without taking account of the available CPU resources on the system. When running parallel pytests (with pytest-xdist), this resulted in the host system being vastly overcommitted: system load would skyrocked, and on a 64-core Linux system, it would take 45 minutes to run
pytest -n auto -m "not slow".This PR changes the way that available CPUs are counted to include a check for whether parallely pytest workers are being used. This is done in two ways:
Change the OpenFermion classes that use multiprocessing directly (e.g.,
src/openfermion/linalg/linear_qubit_operator.py)Use the threadpoolctl package to limit how many threads native libraries such as BLAS use, so that packages like SciPy and NumPy don't end up using threads without control.
Additional changes:
Add the threadpoolctl package to the set of pytest dependencies in
dev_tools/requirements/deps/pytest.txt.Move
src/openfermion/conftest.pyto the top level, for more consistent pytest behavior when it's invoked in different ways.Change
check/pylint-changed-filesandcheck/pytest-and-incremental-coverageto not use parallism by default, and to pass command line flags to pylint and pytest so that users or calling scripts can control when flags like-n autoare used. This makes them more consistent with other scripts incheck/.Change
check/allto pass flags for parallel execution to the other scripts, and to accept a new option--no-parallelto turn off that behavior. In addition, makecheck/allaccept short-form argument names, shorten--apply-format-changesto--apply-changes, and rewrite some non-Bash constructs to be consistently Bash throughout.Update
CONTRIBUTING.mdto mention the use of-n autowith pytest, and the use ofcheck/allinstead of a long list of individual final checks.Some of this work was done with the help of Gemini CLI.