Repository navigation
lightspeed: trace child Python processes in the coverage pass - #9
Merged
Merged
Conversation
Benchmarks that run project code in a child interpreter (for example 'python -c "import pkg"' to time imports) had no Python files in the dependency DB. During the coverage pass, child interpreters now start coverage from COVERAGE_PROCESS_START via a temporary sitecustomize.py, write parallel data files, and their executed lines are added to the benchmark's dependencies. The environment is restored before the timing pass.
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.
Purpose
The coverage pass records which project files each benchmark executes. It only measured the benchmark's own process. Some benchmarks run project code in a child Python process. An example is scikit-image's
benchmark_import_time.ImportSuite.time_import, which runssubprocess.run(f"{sys.executable} -c 'import {pkg}'", shell=True). For these benchmarks the dependency DB had no Python files of the project, so a patch that changes exactly what they measure did not select them.This change measures child Python processes too, during the coverage pass only.
How it works
flowchart LR A[survey_one] --> B[temp dir: coveragerc + sitecustomize.py] B --> C[set COVERAGE_PROCESS_START and PYTHONPATH] C --> D[run benchmark under coverage] D --> E[child python starts coverage, writes .coverage.* file] E --> F[read child lines, add to parent lines] F --> G[restore environment, delete temp dir] G --> H[fingerprints and deps DB as before]survey_onemakes a temporary directory with a coverage config file (parallel = True, data file in that directory, samesourceandomitas the parent) and asitecustomize.py.COVERAGE_PROCESS_STARTpoints to that config and the directory is put first onPYTHONPATH. A child Python interpreter then imports thesitecustomize.py, which callscoverage.process_startup(). After that it runs the environment's ownsitecustomize, if there is one, because ours hides it.Changes
asv/contrib/lightspeed/survey.py: new_child_coveragecontext manager and_child_linesreader.time_importgot only Cython and C rows from the extension scan.survey_one: the parent coverage object also disables theno-data-collectedwarning.-W error) before the child data could be added.test/test_lightspeed_survey.py: new testtest_child_process_files_are_deps.python -c 'import pkg.child'in a shell must listchild.pyas a dependency, andos.environmust be the same after the pass.benchmarkspackage fromsys.modules, so two tests can each load their own temp suite.Verification
pytest test/test_lightspeed_survey.py test/test_lightspeed_project_imports.py: 7 passed (Python 3.12, coverage 7.16.2).pytest test/test_lightspeed_survey.pyon Python 3.8 with coverage 7.5.4 and with coverage 5.5: 2 passed each. These versions do not install coverage's owna1_coverage.pth, so this checks thesitecustomize.pypath.no_source_root_coverage, or theno-data-collectedwarning under the test suite's-W error).asv initialize_diffcheckon a small project with one benchmark that importspkg.childin a child process. The deps DB haspkg/child.pyfor that benchmark, the baseline timing is recorded, and no temporary directories remain.sitecustomize.pythat is already onPYTHONPATHstill runs in the child.Notes
.pthfile that already callsprocess_startup()whenCOVERAGE_PROCESS_STARTis set. Oursitecustomize.pythen calls it a second time, and that call does nothing. With older coverage, our file is the one that starts it.python -Ior-E, children given their ownenv=withoutPYTHONPATHandCOVERAGE_PROCESS_START, interpreters without coverage installed, and children still running after the benchmark function returns.CoverageData.update. With coverage 5.5, an update into the in-memory parent data lost all lines.