perf(callaway-santanna): chunk multiplier-bootstrap weight generation to bound peak memory#561
Conversation
Overall Assessment✅ Looks good — no unmitigated P0/P1 findings. Executive Summary
MethodologyFinding: None
References checked: Code QualityFinding: None
PerformanceFinding: Deferred chunking gaps are tracked
MaintainabilityFinding: None
Tech DebtFinding: Tracked deferred work only
SecurityFinding: None
Documentation / TestsFinding: Local tests not executed
|
4b15d4b to
4c6f20a
Compare
|
🔁 AI review rerun (requested by @igerber) Head SHA: Overall Assessment✅ Looks good — no unmitigated P0/P1 findings. Executive Summary
MethodologyFinding: CS chunking is methodology-equivalent
Finding: Survey df citation regressed
Code QualityFinding: None
PerformanceFinding: Deferred non-CS/stratified chunking gaps are tracked
MaintainabilityFinding: None
Tech DebtFinding: Tracked only
SecurityFinding: None
Documentation / TestsFinding: Survey methodology validation was removed/downgraded
Finding: References page no longer lists cited survey sources
Finding: Tests not executed in this review
|
… to bound peak memory The CS multiplier bootstrap materialized a dense (n_bootstrap x n_units) multiplier-weight matrix. For the default unit-level bootstrap (cluster=None, equivalently cluster="unit": each unit its own PSU) this is generated by the survey-PSU path and dominates peak memory at large n_units (999 x 5M x 8 = ~40 GB -> hard OOM). Generate and consume the weights one draw-block at a time via a new diff_diff/bootstrap_chunking.py helper, capping the live intermediate at O(block x n_units). The weight stream is bit-identical on both backends (Rust absolute per-row seeding; NumPy in-order stream); end-to-end bootstrap SEs match the un-chunked path to within BLAS floating-point reassociation (~1 ULP, far below bootstrap Monte-Carlo error). The unit-as-PSU expansion skips its identity-permutation copy to keep the block at its budgeted ceiling. Measured peak RSS at 999 reps: 500k units 11.6 GB -> 2.4 GB (-79%); 1M units 10.8 GB -> 3.4 GB (-68%). Unstratified survey/cluster generation is tiled; stratified designs (few PSUs) fall back to full generation + sliced blocks (deferred in TODO.md). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
4c6f20a to
173289b
Compare
|
🔁 AI review rerun (requested by @igerber) Head SHA: Overall Assessment✅ Looks good — no unmitigated P0/P1 findings. Executive Summary
MethodologyFinding: CS bootstrap chunking is methodology-equivalent
Finding: Survey PSU bootstrap contract preserved
Code QualityFinding: No new inference anti-patterns found
PerformanceFinding: Remaining non-CS/stratified chunking work is tracked
MaintainabilityFinding: Chunking helper is scoped and documented
Tech DebtFinding: Tracked only
SecurityFinding: None
Documentation / TestsFinding: Prior survey documentation/test regressions appear resolved
Finding: Tests not executed in this review
|
…ng work Adds benchmarks/speed_review/bench_memory_scaling.py -- a subprocess-isolated peak-RSS sweep (ru_maxrss, median of repeats) for the bootstrap-chunking and within-transform memory wins (B1 #561 / B2 #563 / C #567) -- plus a "Memory scaling" section in docs/performance-plan.md with the honest before/after table. Measured pre-#561 (un-optimized) vs current main, 999 bootstrap reps: - CallawaySantAnna bootstrap: 500k 12.9->2.1 GB (-84%), 1M 13.5->3.0 GB (-78%) - EfficientDiD bootstrap 500k: 8.3->1.6 GB (-81%) - HeterogeneousAdoptionDiD cband 500k: 7.7->1.2 GB (-84%) - TWFE within-transform fit: 500k 1.0->0.93 GB (-8%), 1M 1.7->1.5 GB (-9%) Closes the 3-PR memory-scaling initiative. The baseline is re-measured against the pre-#561 tree (not a monkeypatch of current code, which would be contaminated by the same-line optimizations). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ng work Adds benchmarks/speed_review/bench_memory_scaling.py -- a subprocess-isolated peak-RSS sweep (ru_maxrss, median of repeats) for the bootstrap-chunking and within-transform memory wins (B1 #561 / B2 #563 / C #567) -- plus a "Memory scaling" section in docs/performance-plan.md with the honest before/after table. Measured pre-#561 (un-optimized) vs current main, 999 bootstrap reps: - CallawaySantAnna bootstrap: 500k 12.9->2.1 GB (-84%), 1M 13.5->3.0 GB (-78%) - EfficientDiD bootstrap 500k: 8.3->1.6 GB (-81%) - HeterogeneousAdoptionDiD cband 500k: 7.7->1.2 GB (-84%) - TWFE within-transform fit: 500k 1.0->0.93 GB (-8%), 1M 1.7->1.5 GB (-9%) Closes the 3-PR memory-scaling initiative. The baseline is re-measured against the pre-#561 tree (not a monkeypatch of current code, which would be contaminated by the same-line optimizations). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Summary
(n_bootstrap × n_units)weight matrix is never materialized. Newdiff_diff/bootstrap_chunking.pyhelper (iter_weight_blocks,iter_survey_multiplier_weight_blocks,compute_block_size);staggered_bootstrap.pyrefactored to tile the bootstrap over draws into preallocated small output arrays.Methodology references
docs/methodology/REGISTRY.mdCallawaySantAnna).Validation
tests/test_bootstrap_chunking.py(new, 33 tests both backends): block sizing, weight-stream bit-identity (chunked vs single-block and vs the legacy generator), survey-helper parity vsgenerate_survey_multiplier_weights_batch(unstratified tiled,psu=None, FPC scaling, stratified fallback), and end-to-end CS chunk-invariance forcluster="unit",cluster=None(default), and non-identity cluster fan-out — covering ATT(g,t), overall, event-study, group, and cband.tests/test_methodology_callaway.pyimport repointed to the canonical generator. All pass underDIFF_DIFF_BACKEND=rustand=python.TODO.mdActionable Backlog → Performance): stratified survey-PSU generation and the EfficientDiD/HAD bootstraps still build full weight matrices; wiring those through the new helper is follow-up work.Security / privacy