REVIEW 4 major objections 5 minor 17 references
Low Overhead Allocation Sampling in a Garbage Collected Virtual Machine
T0 review · 4 major / 5 minor · reviewed 2026-08-15 · deepseek-v4-flash
Pith's one-line read The paper shows that the check for whether an allocation should be sampled can be folded into the garbage collector's existing nursery-limit check, making per-allocation sampling free.
desk verdict Clever, honest systems paper with a real 'free check' for allocation sampling; evaluation needs more runs and explicit large-object handling, but the mechanism holds and it deserves peer review. read the letter →
The pith
A machine-rendered reading of the paper's core claim, the machinery that carries it, and where it could break.
The reading
What carries the argument
The load-bearing object is a second pointer, the sample point, placed at $\mathit{sample\_point} = \mathit{nursery\_free} + \mathit{sample\_n\_bytes}$ at startup, together with the invariant that the number of bytes until the next sample equals $\mathit{sample\_point} - \mathit{nursery\_free}$. Its role is to make the nursery-limit comparison do double duty: the nursery limit is set to the minimum of the sample point and the real nursery top, so the existing allocation fast-path check fires both when the nursery is exhausted and when a sample is due, leaving the fast path unchanged. The paper notes that the pseudocode is somewhat simplified and that the real implementation must also handle objects larger than the sampling period, which need to be sampled more than once.
What would settle it
On a workload whose total allocation is known, run the profiler and compare the number of samples against total allocated bytes divided by the sampling period; a mismatch, especially when objects larger than the sampling period are allocated, would show that the invariant or the multi-sample case is not implemented as claimed.
Extended reading notes
Core claim
Allocation sampling can be made statistically accurate at the garbage-collector level with no per-allocation cost by reusing the nursery full check. The implementation introduces a sample point inside the nursery and keeps the invariant $\mathit{sample\_point} - \mathit{nursery\_free} = \mathit{sample\_n\_bytes} - \mathit{allocated}$, so the number of bytes until the next sample is exactly the gap between the current nursery pointer and the sample point. Setting the nursery limit to the lower of the sample point and the real nursery top means the existing overflow check fires both when a sample is due and when the nursery is truly full; the collector distinguishes the two cases, records a stack sample when needed, advances the sample point, and then either resumes allocation or performs a minor collection. For sampling periods larger than the nursery, the sample point lies outside the nursery and is adjusted by the change in the nursery free pointer at each minor collection; for large objects allocated outside the nursery, the sample point is moved left by the object size. The paper reports a measured maximum time overhead of 25% at a 4 MB sampling period, with slightly better overhead than time-based sampling when normalized to 1000 samples per second.
Load-bearing premise
Everything rests on the invariant that the gap between the nursery pointer and the sample point exactly tracks the bytes still to be allocated before a sample, and that this invariant survives minor collections, out-of-nursery allocations, and sampling periods larger than the nursery; the pseudocode leaves objects larger than the sampling period to the real implementation.
Editorial extensions
If this is right
- At a 4 MB sampling period, enabling allocation sampling costs at most 25% extra time on the measured benchmarks, and lower overheads are available by raising the period.
- The allocation fast path is the same with and without sampling, so sampling can remain enabled and only the slow path pays for stack walking and bookkeeping.
- Each sample records the call stack, the object type, and whether the object died before or survived the next minor collection, giving a picture of allocation sites and object lifetimes.
- Because samples are taken at GC events rather than at source or bytecode level, the profile reflects allocations that actually happen after JIT escape analysis, avoiding the distortion of instrumenting every allocation site.
- Combining allocation sampling with time sampling gives a dual view of where time is spent and where memory is allocated, and the Guile profiler's approach is the special case where the sampling period equals the nursery size.
Reading between the lines
- A possible generalization: any generational collector with a bump-pointer nursery and a limit check could carry a sample point, so the zero-cost-per-allocation property plausibly extends beyond this particular virtual machine.
- If the invariant is maintained, the sample count on a test workload should equal total allocated bytes divided by the sampling period; testing that equality under randomized sampling periods and object sizes would directly probe the edge cases the pseudocode leaves out.
- The recorded type-and-survival data could feed automatic pretenuring heuristics or lifetime-based allocation advice, a direction the paper mentions only as prior work.
- Since large objects are sampled by moving the sample point left, an object larger than the sampling period should produce multiple samples; verifying that behavior would be a concrete test of the implementation's completeness.
Signed reviews
Editorial analysis
A structured set of objections, weighed in public.
Referee Report
Summary. The paper presents a sampling allocation profiler integrated into PyPy's generational GC. The main idea is to set the nursery limit to a sample_point so that the existing fast-path comparison nursery_free > nursery_limit also decides whether an allocation sample should be taken; when the limit is reached, collect_and_reserve distinguishes a real minor-collection trigger from a sampling trigger. Large-object allocations are sampled by moving sample_point left and checking against nursery_free. The profiler records call stacks, RPython-level object types, and whether the sampled object survived a minor collection, and a converter exports the data to the Firefox Profiler UI. Evaluation on four benchmarks with five runs each reports overhead as a function of sampling period, with a maximum of 25% at a 4 MB sampling period, and a case study in the PyPy JIT demonstrates an allocation optimization.
Significance. If the implementation matches the claims, this is a practically valuable contribution: it shows how allocation-site sampling can be folded nearly for free into the existing bump-pointer check in a generational collector, and it enriches profiles with type and survival information that are useful for managed-language performance work. The work is open source, uses a fuzzer for correctness testing, and honestly labels the evaluation as preliminary. The overhead is measured directly against un-profiled execution, and there are no fitted parameters, so the central empirical claim is not circular. The main risks are that the simplified pseudocode hides exactly the edge cases on which the correctness invariant depends, and the empirical basis for the headline overhead number is thin.
major comments (4)
- [§3.2–3.4, Figs. 8 and 11, footnotes 10–11] The paper's central claim that the sampling check is free depends on the invariant sample_point - nursery_free = sample_n_bytes - allocated being preserved on every allocation path. The pseudocode in Figs. 8 and 11 is explicitly simplified and does not handle an allocation larger than the sampling period, which needs to be sampled more than once; the real code is not shown, and the fuzzer section does not report whether this boundary case was exercised. As written, the paper leaves a gap between the stated invariant and a verifiable implementation. Please include the actual code paths (or a precise specification) for oversize allocations and for sample points outside the nursery, together with fuzzer statistics covering these cases.
- [§3.2 and Fig. 4] The fast-path check is `gc.nursery_free > gc.nursery_limit`, so an allocation that makes the cumulative allocated bytes exactly equal to `sample_n_bytes` is not sampled; the sample is deferred to the next allocation. This is a real off-by-one issue when object sizes divide the sampling period, and it systematically misattributes the sample away from the object containing the boundary byte. The paper should state whether this is intentional and, if so, justify that it does not bias the profile; otherwise the comparison should be `>=`.
- [§5.1, Fig. 13] The headline overhead claim ('maximum time overhead of 25%' at a 4 MB sampling period) is based on five runs of each of four benchmarks, with no error bars, no per-run distribution shown, and no statement of which benchmark produced the maximum. Because overhead measurements in JITted virtual machines are noisy, this is not enough statistical support for a quantitative headline claim. Please report medians and spreads, or per-run values, and state whether 'maximum' means the worst observed run across all benchmarks.
- [§3.3] The case `sample_n_bytes > nursery_size` is described only in prose and Figure 10; there is no pseudocode for adjusting `sample_point` across minor collections or for deciding when to take a sample when `nursery_limit != sample_point`. Since the paper explicitly supports sampling periods larger than the nursery, this path must be specified precisely and tested, otherwise the correctness of the 'free check' claim for long periods cannot be assessed.
minor comments (5)
- [Abstract and §5.1] The abstract states the 25% maximum without the qualification 'preliminary evaluation' that appears in §5.1; please qualify the headline claim in the abstract as well.
- [§5.1] The phrase 'high sampling period (high period = low value for sample_n_bytes)' is inconsistent: a high sampling period should mean a large value of `sample_n_bytes`. Please correct the wording.
- [§5.1, Fig. 16] The formula for normalized overhead is ambiguous as written; use `1 + (overhead - 1) * 1000 / num_samples` with explicit parentheses, and note that this assumes the per-sample overhead is independent of the sampling rate.
- [Figure 1 caption] The caption contains the duplicated phrase 'show 7 show' and several other grammatical issues; please clean it up.
- [Figure 7 and §3.2] Figure 7 draws multiple sample points, while the text states that only one `sample_point` exists at any time; please clarify that the figure is a conceptual illustration rather than a literal snapshot.
Circularity Check
No circularity: the free-check claim is an implementation construction verified by fuzzing, and the overhead numbers are direct measurements without fitted parameters.
full rationale
The paper's central claim is that the sampling check can be folded into the existing nursery bump-pointer limit check, leaving the allocation fast path unchanged. This is presented as an engineering construction, not as a derived prediction: sample_point is initialized as nursery_free + sample_n_bytes, nursery_limit is set to min(sample_point, nursery_top), and the invariant sample_point - nursery_free = sample_n_bytes - allocated follows by arithmetic and is maintained by the shown code. The figure 4 fast path is identical with and without sampling, so the 'free check' claim is a property of the implemented mechanism rather than a conclusion derived from its own output. The overhead evaluation compares runtime with sampling to runtime without sampling on the same benchmarks; there are no fitted parameters, no subset of data used to predict another subset, and no self-citation is load-bearing. The references to the authors' earlier JIT work are background on escape analysis and PyPy's tracing JIT, not justifications of the sampling result. Correctness is checked by a randomized fuzzer against independently computed expected sample triggers. The noted simplifications for objects larger than the sampling period are robustness concerns, not circularity. No step in the paper reduces to its own inputs by definition or by self-citation.
Assumptions & free parameters
assumptions (3)
- domain assumption PyPy's GC uses a single contiguous nursery with bump-pointer allocation, so the nursery limit check is a single comparison.
- domain assumption The branch on nursery overflow is predictable and cheap, so reusing it for sampling does not slow the fast path.
- domain assumption The benchmark suite (four programs) is representative of allocation-heavy workloads.
Cite this review
Pith. "Pith review of Low Overhead Allocation Sampling in a Garbage Collected Virtual Machine." pith.science (2026). https://pith.science/paper/YGK422YT
@misc{pith2026250616883,
author = {Pith},
title = {Pith review of: Low Overhead Allocation Sampling in a Garbage Collected Virtual Machine},
year = {2026},
howpublished = {\url{https://pith.science/paper/YGK422YT}},
note = {Machine review of arXiv:2506.16883}
}
read the original abstract
Compared to the more commonly used time-based profiling, allocation profiling provides an alternate view of the execution of allocation heavy dynamically typed languages. However, profiling every single allocation in a program is very inefficient. We present a sampling allocation profiler that is deeply integrated into the garbage collector of PyPy, a Python virtual machine. This integration ensures tunable low overhead for the allocation profiler, which we measure and quantify. Enabling allocation sampling profiling with a sampling period of 4 MB leads to a maximum time overhead of 25% in our benchmarks, over un-profiled regular execution.
Figures
Figures from the paper (11 more)
Reference graph
Works this paper leans on
-
[1]
Davide Ancona, Massimo Ancona, Antonio Cuni, and Nicholas D. Matsakis. 2007. RPython: a step towards reconciling dynamically and statically typed OO languages. InProceedings of the 2007 Symposium on Dynamic Languages (Montreal, Quebec, Canada)(DLS ’07). Association for Computing Machinery, New York, NY, USA, 53–64. doi:10.1145/ 1297081.1297091
arXiv 2007
-
[2]
backtrace labs. 2020. Poireau GitHub. https://github.com/backtrace- labs/poireau
work page 2020
-
[3]
bloomberg. 2022. memray GitHub. https://github.com/bloomberg/ memray 24https://github.com/Cskorpion/microbenchmark 25https://github.com/pypy/pypy/blob/main/rpython/translator/goal/ gcbench.py
work page 2022
-
[4]
Carl Friedrich Bolz, Antonio Cuni, Maciej Fijałkowski, Michael Leuschel, Samuele Pedroni, and Armin Rigo. 2011. Allocation re- moval by partial evaluation in a tracing JIT. In Proceedings of the 20th ACM SIGPLAN Workshop on Partial Evaluation and Program Manip- ulation (Austin, Texas, USA)(PEPM ’11). Association for Computing Machinery, New York, NY, USA,...
arXiv 2011
-
[5]
Carl Friedrich Bolz, Antonio Cuni, Maciej Fijałkowski, and Armin Rigo. 2009. Tracing the meta-level: PyPy’s tracing JIT compiler. In Proceedings of the 4th Workshop on the Implementation, Compilation, Optimization of Object-Oriented Languages and Programming Systems (Genova, Italy)(ICOOOLPS ’09). Association for Computing Machinery, New York, NY, USA, 18–...
arXiv 2009
-
[6]
Carl Friedrich Bolz-Tereick, Luke Panayi, Ferdia McKeogh, Tom Spink, and Martin Berger. 2025. Pydrofoil: accelerating Sail-based instruction set simulators. arXiv:arXiv:2503.04389 To appear in ECOOP 2025
work page Pith review arXiv 2025
-
[7]
Humphrey Burchell, Octave Larose, and Stefan Marr. 2024. Towards Realistic Results for Instrumentation-Based Profilers for JIT-Compiled Systems. In Proceedings of the 21st ACM SIGPLAN International Con- ference on Managed Programming Languages and Runtimes (Vienna, Austria) (MPLR 2024). Association for Computing Machinery, New York, NY, USA, 82–89. doi: 1...
- [8]
Show all 17 references
-
[9]
Richard Jones, Antony Hosking, and Eliot Moss. 2023. The Garbage Collection Handbook: The Art of Automatic Memory Management (2nd edition ed.). Chapman & Hall/CRC, Boca Raton
2023
-
[10]
Blackburn, and Kathryn S
Maria Jump, Stephen M. Blackburn, and Kathryn S. McKinley. 2004. Dynamic object sampling for pretenuring. In Proceedings of the 4th International Symposium on Memory Management (Vancouver, BC, Canada) (ISMM ’04). Association for Computing Machinery, New York, NY, USA, 152–162....
2004
-
[11]
MacIver and Alastair F
David R. MacIver and Alastair F. Donaldson. 2020. Test-Case Reduction via Test-Case Generation: Insights from the Hypothesis Reducer. In 34th European Conference on Object-Oriented Programming (ECOOP
2020
-
[12]
MacIver, Zac Hatfield-Dodds, and many other contributors
David R. MacIver, Zac Hatfield-Dodds, and many other contributors
-
[13]
Armin Rigo and Samuele Pedroni. 2006. PyPy’s approach to virtual machine construction. In DLS. ACM, Portland, Oregon, USA. doi: 10. 1145/1176617.1176753
2006
-
[14]
Itamar Turner-Trauring. 2024. Sciagraph homepage. https://www. sciagraph.com/docs/reference/limitations/
2024
-
[15]
Paul R. Wilson. 1992. Uniprocessor Garbage Collection Techniques. In Proceedings of the International Workshop on Memory Management . Springer-Verlag, 1–42. http://portal.acm.org/citation.cfm?id=664824
1992
-
[2019]
Hypothesis: A new approach to property-based testing. (Nov. 2019). doi:10.21105/joss.01891
2019 doi
-
[2020]
166), Robert Hirschfeld and Tobias Pape (Eds.)
(Leibniz International Proceedings in Informatics (LIPIcs), Vol. 166), Robert Hirschfeld and Tobias Pape (Eds.). Schloss Dagstuhl – Leibniz- Zentrum für Informatik, Dagstuhl, Germany, 13:1–13:27. doi: 10.4230/ LIPIcs.ECOOP.2020.13
Reviewed August 15, 2026 · model on record in the stance chip above.
Discussion (0). Continue with ORCID to comment.