Repository navigation
Conversation
This was referenced Oct 11, 2026
MaxGhenis
force-pushed
the
ci-test-memory
branch
from
October 11, 2026 18:38
9bd2bf2 to
fea4062
Compare
Coverage's sys.monitoring core (COVERAGE_CORE=sysmon on CI) keeps every code object it has seen alive. policyengine-core compiles a system's variable files again for every new system, so each Simulation left about 2,500 code objects behind and the test shards grew to the runner's memory. The conftest now returns the code object compiled the first time for each variable file, so every system runs the same code with its own modules, classes and functions. Fixes #2330 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A property test on CI had 165 systems' variable modules loaded at once, because dead systems' modules were released only after each test, and Hypothesis builds hundreds of systems while it shrinks a failing example. The loader hook now also runs release_dead_variable_modules() when a new system starts loading its variables, so at most max_owners dead systems' modules are held. Also record the CI before/after numbers in the comment. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On CI the worker that ran the microsimulation tests kept about 4 GiB of RSS after their simulations were gone, though locally only the 0.74 GiB cached dataset stayed referenced. The conftest now calls glibc's malloc_trim(0) once a file's fixtures are torn down, and the latest-data smoke test removes its unpinned dataset from the simulation cache when its module ends, since no other test uses it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
pytest_sessionstart never reaches a conftest that pytest finds only while collecting, as with a bare `pytest` from the repository root, so the hook was not installed there and the sharing and release tests failed. pytest_configure is also called for conftests registered later. The loader is restored in the existing pytest_unconfigure (a second definition would replace it). Also read coverage's tracers with getattr in the test helper. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MaxGhenis
force-pushed
the
ci-test-memory
branch
from
October 11, 2026 19:38
a113a0e to
7e96e72
Compare
Opus review of 7e96e72: - The release ran from the SourceFileLoader hook, which policyengine-core#608 bypasses, so it would have stopped without notice. It now runs from TaxBenefitSystem.add_variables_from_file as each new system starts adding its variables. - With more than eight systems alive (an lru_cache of systems in a test module), every new system and every test scanned the heap and released nothing; a worker's later tests took 432 s instead of 219 s. The release now scans only once eight more systems own modules than were alive at the last scan, after tests as well as builds. - The release test assumed at most eight live systems and failed in a serial run; it now measures the live systems first. - Sharing tests skip, with a note, once policyengine-core shares the code; the note names the right function. - The malloc_trim test checks for glibc rather than Linux, malloc_trim gets its C signature, the loader restore leaves no copy on the subclass, the latest-data fixture keeps the cache when the latest URL is the pinned one, and comments say "about the same time" and "can build many more". Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Opus delta review of 4a88e23 (approved) left three nits: the comment was off by one (a scan needs more than max_new owners beyond the live ones); the release test could pass if builds reused dead systems' ids, so it now also asserts a release ran; and nothing pinned the amortization, so a Hypothesis test models builds, deaths and releases with faked owner counts and checks that a scan happens exactly when more than max_new owners are beyond the live ones and that at most max_new are left after. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MaxGhenis
added a commit
that referenced
this pull request
Oct 11, 2026
…tests Not for merging. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Oct 11, 2026
Open
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MaxGhenis
marked this pull request as ready for review
October 11, 2026 21:44
Collaborator
Author
|
Hand-off to the UK hub (session "Cut peak memory of pe-uk CI test shards").
|
This was referenced Oct 12, 2026
MaxGhenis
added a commit
that referenced
this pull request
Oct 12, 2026
…on under sysmon coverage) at 4d472d9
This was referenced Oct 12, 2026
Collaborator
Author
|
UK hub (2026-10-12 11:05Z): this PR's head |
This branch has not been deployed
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.
Fixes #2330
Stacked on #2317 (which is stacked on #2269). The diff against
ci-test-gc-freezeis this PR's change alone. Test code only.Cause
Coverage's
sys.monitoringcore, which #2269 turns on withCOVERAGE_CORE=sysmon, keeps every code object it has seen alive, so it can key its records byid(code)(SysMonitor.code_objects). policyengine-core compiles a system's ~1,110 variable files again for each new system, and everySimulationbuilds one. By the end of a CI shard, each pytest-xdist worker's coverage held 0.8 to 1.46 million code objects, and the shards used up to 1.8 GiB of swap. Details in #2330.Change
policyengine_uk/tests/conftest.py:SourceFileLoader.get_codeis wrapped for policyengine-core's per-system variable modules only (names<id>_<hash>_<file>).pytest_configureand removed in itspytest_unconfigure. So it also applies when a barepytestfinds the conftest during collection.release_after_new_systems()runsrelease_dead_variable_modules()once more than eight systems own modules beyond those alive at the last release.TaxBenefitSystem.add_variables_from_file) and after each test.lru_cacheof systems) no longer scans the heap after every build and test.malloc_trim(0)returns free heap memory; elsewhere this does nothing. The microsimulation worker had kept ~4 GiB of RSS after its simulations died, though locally only the 0.74 GiB cached dataset stayed referenced (tracemalloc).test_latest_data_smoke.pystops keeping its unpinned dataset in_url_dataset_cacheafter its module. No other test uses that URL, and the cache entry is kept if the pinned dataset is set to the same URL.The upstream fix for (1) is PolicyEngine/policyengine-core#608. Once it ships, the tests of the wrapper skip with a note to remove it, and the release (2) keeps working, because it no longer depends on the loader.
Measured on CI
Each run built one commit in every configuration at the same time. The 2 s runner sampler is from
gc-measure/2317; a per-worker probe logs RSS after every system build and every test. The runner has 15.6 GiB.test_an_exported_frame_loads_back_and_exports_itself, which failed on this base, was deselected in every run, so every figure covers the suite without it. It is fixed on main in 3b8f54b.PYTHONDONTWRITEBYTECODE=1and no__pycache__, so it compares against compiling every file for every system. With valid.pycfiles the per-system saving is smaller; the CI wall times above are the measured effect.Invariants (tests)
test_variable_code_sharing.py:compile()of its current source, and every variable file is covered.Simulationadds fewer than 100 code objects to coverage, against more than 1,000 with sharing turned off.test_variable_module_release.py:max_newowners are beyond the live ones, and at mostmax_newremain after.test_malloc_trim.py: it trims after a file's last test and not between tests of one file, and with glibc it findsmalloc_trim.Review
An independent Opus 5.5 review (
~/reviews/uk-ci-test-memory-2026-10-11/review-2331-opus-*.md) went through three rounds:Follow-ups (not in this PR)
pr_code_changes.yamlcomment. It keeps four shards for wall time and is citing these measurements in Publish only the policyengine-uk release files whose checks passed #2306.test_aggregate_impact_certified.pyand moves the pin to 1.58.0. I'll re-measure once both land.lru_cacheof systems intest_two_child_limit_age_exemption_properties.py, about 0.55 GiB per worker.axiom: n/a: test infrastructure
🤖 Generated with Claude Code