Minor heap sizing after the OCaml 5.x multicore GC transition: retuning -I for parallel workloads
0 reputation · 13 Oct 2023, 16:04 UTC
0 reputation · 13 Oct 2023, 16:04 UTC
OCaml 5.0 replaced the single-threaded runtime with a multicore-capable one, including a parallelized garbage collector where each domain allocates into its own minor heap arena before promotion to the shared major heap. Tuning guidance written for the 4.x runtime — for example, enlarging the minor heap via the -I runtime parameter or OCAMLRUNPARAM to reduce promotion pressure — no longer maps cleanly onto this model.
The goal is to establish a defensible sizing policy for the per-domain minor heap on a service running several domains with allocation-heavy, short-lived workloads, where the legacy heuristic of "grow the minor heap until major collections become rare" was tuned against a single stop-the-world collector. It is unclear whether per-domain minor heaps should be sized independently of domain count, or whether aggregate arena memory and synchronization of the major collection phase change the trade-off. Gc.stat counters are available for observation, but their interpretation across domains is not obvious.
Assuming OCaml 5.1+ and stock runtime flags:
-I minor heap setting applied per domain, and does the optimal value scale down as domain count increases?Gc statistics best indicate that minor heap size, rather than cross-domain synchronization, is the bottleneck?35275 reputation · 14 Oct 2023, 01:35 UTC
-I (or OCAMLRUNPARAM=i=...) sets the size of each domain's private minor heap arena — it is not a global budget. On OCaml 5.1+, the value does not need to shrink as you add domains; the correct mental model is "the 4.x heuristic, applied per domain, with total memory now scaling linearly with domain count." The old rule — grow the minor heap until major collections become rare — still works, but you evaluate it per domain and watch aggregate memory instead of a single heap.
Yes, per domain. Every domain allocates into its own minor heap arena and promotes survivors into the shared major heap, so eight domains at -I 256k words reserve roughly eight times the arena memory of one domain. That has two consequences:
-I across 16 domains to fit RAM usually costs more in minor GC churn than running fewer, well-fed domains.Gc.stat aggregates counters across all domains, so normalize by active domain count (or use per-domain stats where your version exposes them) before comparing. The useful signals:
promoted_words / minor_words. A persistently high ratio (roughly above 0.5) means too much survives each minor cycle — the arena is too small and you're paying promotion and major-heap pressure for it. Getting it under ~0.3 is a reasonable target for short-lived workloads.minor_collections climbing steeply while major_collections stays flat is the classic undersized-minor-heap signature. If minor collections are rare but major collections are frequent, your problem is major heap sizing or live data volume — enlarging -I won't fix it.major_collections and compactions, plus wall-clock stalls that don't track allocation rate.The promotion policy itself is conceptually familiar — survivors of a minor collection are copied to the major heap — but the structure changed: one global stop-the-world minor heap became per-domain arenas promoting into a shared major heap collected by a parallel mark-and-sweep. Two practical invalidations of 4.x folklore:
Gc.stat at steady state, and compute per-domain promotion ratio and minor collection rate.-I (e.g., 64k, 128k, 256k, 512k words) via OCAMLRUNPARAM=i=... and pick the smallest value where the promotion ratio drops below ~0.3 and minor collections plateau.major_collections did not rise and that total arena memory (-I × domains) fits your host budget; if tracing is available in your build, check per-domain occupancy stays comfortably below the arena size.One caveat worth flagging: exact counter semantics and the availability of per-domain statistics vary across 5.x point releases, so verify against the Gc module documentation for your specific compiler version before baking numbers into production config.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.