Repository navigation
Cut the behavioural-response copy tests' CI time - #10440
Conversation
Raise the cycle collector's first threshold while this module copies policy trees, and run each pinned copy case for all three wage levels in one three-household simulation, replacing three derandomized generated examples that re-ran pinned paths at other wages. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Expected responses come from a new one-household reform simulation per wage level, as before; the three are shared with the fully-calculated clone test, so the three-household pins add no reference runs. State the GC fixture's measured effect, and why only the pins run. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…-measurement-copies
Review (GPT-6.1 Sol) found that the old derandomized example (clone after measuring a lone 20,000 household, then 50,000) was the only source whose measured response was zero for every household; every three-household source has nonzero households. Pin it again as a fifth example, with the source's wages a parameter once more (a tuple). The fixture docstring now states only saved measurements: 6.4 million tracked objects per tree copy, none garbage after it; 29 against 8 full collections; and the CI effect on the tests this change does not otherwise touch. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…-measurement-copies
Re-measure against core 3.33.2: a tree copy tracks 6.9 million objects, and the sweep ran 28 full collections at the default threshold and 9 at 100,000. Compare CI with the 57 Rest runs that ran this file without the fixture, and record why the threshold stays at 100,000. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The 6.9 million objects cover the whole clone_spm_system call, and the CI figure rounds to 743 s. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
The US + core hub now owns landing this PR. GPT-6.1 Sol (Subfleet, hard tier) approved 97bc1b2 and then the delta to the current head, 6fdd639, with no blocking findings. Its verdicts sit in All six Rest shards passed in run 41143310235 at 9e5416c, the merge with main, and a later push cancelled the rest of that run. Since then this branch has changed only the fixture docstring. Run 41150119839 at the current head has not finished. This PR depends on no other PR and carries no hold or decision. Besides CI, only the required approving review blocks it, so it can land with One follow-up remains: when run 41150119839 finishes, add its Rest timings as a second "after" column in the Timings table. Adding the run's id to |
|
US + core hub merge audit, us#10440 at
|
|
PolicyEngine/policyengine-core#631 merged, and it lowers what these copy tests cost.
The ranges span the two rounds. The host stayed busy, so read them as ranges rather than a percentage. These tests pick up the change once policyengine-us installs a Core release that contains it. |
Cuts the CI time of
policyengine_us/tests/core/test_behavioral_response_measurement_copies.py(#9913) and keeps every case it ran. Only that test file and a changelog fragment change.Main's push run 40031737740 spent 1,512 s of the Rest
coregroup's 43:46 on this file; the group took 11:51 in run 38141803955 on 2026-10-11, before the file existed. Across the 57 Rest runs in the timing artifacts from 2026-10-29 19:45Z to 2026-10-30 23:52Z, the file took from 1,318 s to 1,819 s, with a median of 1,628 s. This PR's run 41143310235 took 1,177 s.Where the time went
The file builds no
Microsimulation: every test builds one-person California households. The time goes to copying policy.clone_spm_systemdeep-copies the policy system, including the whole parameter tree, whenever a test builds a reform simulation, twice per new measurement (the reform branch withclone_system=True, and_baseline_measurement_branch) and once perclone().Parameter.clone(374,838 calls) took 20.5 s cumulative, including 12.9 s inParameterAtInstant.clone(4.0 million calls).metadata(copy.deepcopy(self.metadata)), andParameter.clonealso clones each of its values.copy.deepcopytook 21.4 s over 15.9 million calls.clone_spm_systemcall adds 6.9 million objects the collector tracks, and a full collection right after it frees none of them. Every full collection traverses every live copy. Over two rounds of building, measuring, cloning and measuring a reform simulation (12 copies), collection took 51% of the 115.6 s wall time at the default first threshold of 2,000, and full collections alone took 46%.test_a_copy_reports_the_response_for_its_inputs_and_measurement_statetook a median of 662 s. Undermax_examples=3, derandomize=Trueit ran four explicit pins plus three generated examples. With this test's old strategies, Hypothesis 6.168.3 and CPython 3.14, those three stayed the same on every run. Excluding the one-household references, each example builds a reform simulation, makes up to two measurements and makes one copy.The parametrized tests stay, because each parameter exercises its own construction path. The
[None]/[reform1]tests build simulations with and without a baseline. In the[entity]/[period]tests a reform changes a variable's entity or definition period, and the baseline measurement branch drops the holder that no longer fits baseline policy.Changes
gc.freeze(), with the same threshold) ran 6.8% faster in two paired local runs (415 s against 445 s). This PR leaves it out: for less than a 10% gain,gc.freeze()takes every object alive when the module starts, from whichever modules ran before it in the process, out of collection until teardown.phases=[Phase.explicit]). Four of the five pins build their source as one simulation of three households, at 20,000, 50,000 and 100,000.(50,000, 100,000, 20,000), so each household takes another's wages.fresh_responseconcatenates them). For a branch it creates after the source measures, it checks the source's measured responses scaled by wages, as d1021 requires, and a snapshot of the source's measurements.phasesmakes Hypothesis generate examples from the new strategies, not the old ones: three households in one source, with the copy's wages a permutation of theirs.The test now builds 8 reform simulations, makes 10 measurements and makes 3 clones, against 10, 12 and 4 before, counting the one-household references it builds and measures.
Coverage of each removed or merged case
Each pin keeps its
kindandafter_source_measured. Under this reform a household responds with 0 at 20,000, 315.82 at 50,000 and -52.12 at 100,000. Three households side by side in one simulation return those values in both wage orders the pins use (np.array_equalholds against one-household simulations), so a household in a pin stands in for the one-household case.The pins also run five household cases the old test never ran. They:
Mutation check
A plugin patches the code before the test module imports it, and a runner calls every old case and every new pin one by one under each mutant, at the merge with main (9e5416c). Four mutants re-introduce the three defects the module docstring describes, the first in two forms. Two more break invariant 3 and a clone's own measurement.
clone_system=Truebranch ofget_branch("baseline")_baseline_measurement_branchreturnsget_branch("baseline"))get_branchdeletes the new branch's measurement cachetest_copies_own_their_measurementsandtest_a_branch_measures_a_new_period_without_touching_its_parent, both unchanged, fail under it.Timings
JUnit reports cover the 57 Rest runs in the window above that reported this file: 19 on main and 38 on other PRs. All 57 head commits hold main's version of the test file (blob 58d6c5f2bf). The file ran alone in its process in 47 of them, and those 47 supply process elapsed and max RSS.
test_a_copy_reports_the_response_for_its_inputs_and_measurement_state(s)At 9e5416c, four pytest processes ran concurrently on a shared 18-CPU host in each of two local waves, and each process ran its tests serially.
In the first wave, from before to this PR's version, pytest total fell 27% and peak RSS rose 3.3%. Each wave shared the host's load.
Invariants
The file's four invariants (module docstring) and the tests that check them stay the same. Only the Hypothesis test changes: which households one pinned run covers, plus the lone-household pin.
Shards
#10439 plans the Rest shards from the JUnit reports of recent successful release runs on main. After a successful release run on main records this file's new times, later plans can use them. No Makefile change.
impact: none (test file only; no variable, parameter or reform file changes)
axiom: n/a: test-only CI cost change.
🤖 Generated with Claude Code