How OCaml Locals Use a Separate Stack

A list example shows how region-scoped local values can avoid repeated garbage-collected allocations.

Channel
Jane Street
Published
Feb 29, 2024
Video
11:07
Read
2 min
Slides
3
Original
English
Open on YouTube
Scroll to read and the video shrinks to the bottom-right corner. Captions follow your reading language.
Reading languageAvailable in 2 languages
You're reading the original English.
SUMMARY

In 30 seconds

In this OCaml Unboxed episode, the speaker compares heap allocation and garbage collection with allocation on a separate local stack. The loop example shows how non-escaping values can be reclaimed at the end of a region, and why local values may need to survive a function return.

  1. 1Repeated heap allocationEach iteration builds a new list, leaving the previous list unreachable once the reference changes.
  2. 2The cost of collectionThe simplified mark-and-sweep example illustrates the work and potential pauses involved in reclaiming heap objects.
  3. 3Regions bound lifetimesLocal values allocated inside the loop body cannot escape that region.
  4. 4Why a separate stackAn exclave can allow values to survive a function return while remaining within the enclosing region.

Takeaways

  1. The example allocates repeated lists on the heap when it does not use locals.
  2. Garbage collection reclaims unreachable values but can add work and latency.
  3. A loop body forms a region whose local values cannot escape.
  4. Ending the region permits its local allocations to be reclaimed together and reused.
  5. The local stack is separate from the call stack because some local values outlive the function that allocated them.
FULL TEXT

Read the edited text by chapter

Transcript only
01

A list-allocation example

The speaker revisits a program whose local annotations reduce garbage-collected allocation. The example repeatedly constructs and processes a list.

In this installment of OCaml Unboxed, I continue exploring the local mode that my Jane Street colleagues and I are developing in an open-source compiler branch. A previous example reduced garbage-collected allocations using local annotations. Here I want to show where the memory goes and why that works.

The example’s main function, doit, loops with an iteration variable and a list. It calls init to construct the list and then counts multiples of five. First, imagine the program without local annotations and follow the allocations on the heap.

02

Heap allocation and garbage collection

Without locals, old lists become unreachable and are reclaimed by garbage collection. The speaker explains the cost in a simplified mark-and-sweep model.

init constructs a linked list of cons cells on the heap. The list variable points to its beginning. In the next iteration init constructs another list, and list changes to point to the new one. The old cells are no longer reachable through that variable.

When garbage collection runs, the simplified picture is to trace from roots such as stack variables, mark reachable heap objects and reclaim objects that cannot be reached. The current list remains live; the old list can be collected, making space for later allocations.

Tracing reachable heap cells
SLIDE 1 / 3Tracing reachable heap cellsThe drawing shows the call-stack reference and the reachable and unreachable list cells.

Garbage collection is valuable: it supports immutable functional programming and makes programs easier to reason about and develop. But finding live data and reclaiming the rest takes work. In the mark-and-sweep picture, collection can pause program execution and touch memory that may displace more useful cached data.

Most programs at Jane Street are fine with garbage collection. For latency-sensitive cases, however, we want to avoid those pauses and other slowdowns. That performance goal motivated our investigation of locals.

03

A separate local stack

Local allocations are confined to regions and can be reclaimed together at a region’s end. A separate stack permits values to survive a function return without escaping their region.

Now consider the local version. The call stack still holds doit and its variables, but init allocates the list cells in a separate area I call the local stack. This is neither the ordinary call stack nor the garbage-collected heap.

init finishes and returns the locally allocated list, which doit can use to count multiples of five. The loop body is a region: local values allocated in that region cannot escape it. When the iteration ends, the analysis guarantees that those allocations are no longer needed.

The separate local stack
SLIDE 2 / 3The separate local stackList cells are allocated separately from the call stack and heap.

The runtime can reclaim the region’s local allocations together and reuse the same memory in the next iteration. The deallocation can be as simple as changing the pointer used for the next allocation. It avoids the mark-and-sweep work described in the heap example.

Reclaiming a region
SLIDE 3 / 3Reclaiming a regionThe sketch shows the old local allocations being discarded at the end of an iteration.

The separate local stack matters because init can return before its caller finishes using the list. Values created inside an exclave can survive a function return while still being limited to their enclosing region. Function frames and regions do not always have identical lifetimes, so the local allocations cannot simply live in init’s call-stack frame.

More from this channel

1:48:29EN
JSJane Street· Oct 16, 2026How AI Is Changing Programming: John Krazit and Ron MinskyRon Minsky, John Krazit · Jane StreetJane Street’s AI tooling lead and the Signals and Threads host examine agent infrastructure, security, code review and the changing experience of writing software.Coding agentsDeveloper productivityEvals+2
1:36:37EN
JSJane Street· Sep 23, 2026Aaron Bauer on Teaching Software Engineering in the Age of AIRon Minsky, Aaron Bauer · Jane StreetHow learning goals, active practice and editor design shape engineering education when models can generate the code.AI in EducationSoftware engineeringEducation+2
9 slides1:05:45EN
JSJane Street· Sep 11, 2026Cyrus Omar on Live Programming and a Computational Commons for Humans and AICyrus OmarA demonstration of Hazel shows how immediate feedback, structured collaboration and carefully scoped permissions could support people and AI agents working together.Programming LanguagesHuman-AI CollaborationDeveloper workflows & testing+2
1:25:00EN
JSJane Street· Sep 2, 2026Eric Mannes on Turning Messy External Data Into Trading ResearchRon, Eric Mannes · Jane StreetFrom commodity futures and an Excel risk system to point-in-time datasets and AI, Eric Mannes explains the domain knowledge behind data engineering at Jane Street.Data quality & curationDeveloper workflows & testingInvesting+2
7 slides1:16:29EN
JSJane Street· Aug 26, 2026Grant Sanderson and Alok Puranik: Positional Encodings Through Group TheoryGrant Sanderson, Alok Puranik · 3Blue1BrownStarting with assumptions about attention, they derive matrix exponentials and connect rotations, decay and Jordan blocks to RoPE and ALiBi.Transformers & attentionMathematicsML fundamentals+1
27:39EN
JSJane Street· Aug 21, 20263Blue1Brown and Jane Street on Trading, Machine Learning and CultureGrant Sanderson, Nitya, In Young, Craig, Alok · 3Blue1BrownGrant Sanderson talks with Jane Street colleagues about market making, shared compute, research and the paths that brought them to the firm.Machine LearningFintechCompany Culture+2