En 30 segundos
Chris Casinghino explains the trade-offs of OCaml’s uniform representation and its multicore memory model, then describes Jane Street’s work on unboxed layouts, stack allocation, and data-race freedom. At the 2025 talk, unboxed types and stack allocation were in production, while race-freedom support was beginning rollout.
- 1Uniform valuesClassic OCaml represents a value in one word, either as an immediate or a pointer to a heap block.
- 2The cost of uniformityA byte can occupy a word and a boxed int64 requires a heap block, affecting locality and garbage collection.
- 3Unboxed layoutsKinds describe how types are represented, allowing compact primitives and records while preserving compiler knowledge.
- 4Local and global modesModes track whether a value can escape its scope, enabling safe stack allocation and resource APIs.
- 5Race freedom through modesContention tracks sharing and portability controls whether values can be sent between threads.
- 6Production and rolloutCasinghino says stack allocation and unboxed types were in production, with data-race freedom beginning beta deployment.
Conclusiones
- A uniform one-word value representation simplifies garbage collection and polymorphism but limits control over memory layout.
- Unboxed types and layout kinds let the compiler track narrower and flatter representations.
- Local modes can enforce stack discipline and prevent values from escaping their scope.
- Contention and portability modes can help statically prevent unsafe cross-thread access.
- The design aims to preserve type and memory safety, inference, compatibility, and complexity proportional to use.
- As reported in 2025, some features were already used in production while data-race freedom was in early rollout.
Lee el texto editado por capítulos
OCaml values, polymorphism, and parallelism
Casinghino explains uniform values, type inference, and multicore OCaml. He contrasts OCaml’s bounded data-race behavior with a static race-free guarantee.
I work on Jane Street’s OCaml language team, part of its tools and compilers group. We build libraries and development tools as well as language extensions. Today I will discuss areas where OCaml can improve for high-performance software and the features we are building to address them.
I will first explain some existing OCaml behavior, because understanding its benefits and costs is important to the new design. The first detail is the uniform representation of values in memory.
In the classic representation, each OCaml value occupies one machine word. It is either an immediate, with data packed into the word, or a pointer to a heap block. On a 64-bit machine an immediate has 63 data bits and a tag bit; integers, characters, and booleans use this form.

Values that cannot fit in that representation are represented by pointers to heap blocks, with a header and fields. Uniformity helps the garbage collector distinguish pointers from immediate values and scan the relevant heap data.
OCaml also has expressive static types, inference, and pervasive polymorphism. The map function, for example, applies a function to a list and produces a list of another type. The compiler can infer its generic type without the programmer writing the annotations.
Uniform values make separate compilation of polymorphic functions straightforward: one compiled function can handle types represented by the same one-word calling convention. By contrast, C++ generics commonly create specialized copies for types, trading optimization opportunities for code size and instruction-cache pressure.
Other language implementations can generate specialized code at runtime through just-in-time compilation. Casinghino then turns to OCaml 5, which introduced shared-memory multicore parallelism and required a runtime and garbage collector capable of handling concurrent access.
He describes OCaml’s memory model as bounding the consequences of data races in space and time. In contrast, C++ treats a data race as undefined behavior, with consequences that can be surprising and difficult to localize. A race involves concurrent access to the same memory with at least one write.
Those bounds make debugging easier, but OCaml 5 does not by itself give a static race-free guarantee. Safe Rust offers such a guarantee through its type system. Casinghino wants to bring a similar property to OCaml without losing its familiar character.
Performance goals and unboxed layouts
Uniform representation creates space and locality costs. The team introduces layout kinds and unboxed types while maintaining compatibility and safety.
Uniform representation also limits control over memory layout. Eight bits of data can consume a whole machine word. A boxed int64 cannot fit in the immediate’s 63 bits and, in the example described, needs a three-word heap block including metadata.

Arrays of pointers can scatter data across the heap, impair prefetching and cache locality, while the collector must manage the allocated blocks. OCaml’s collector is fast, but reducing unnecessary allocations can still help performance-sensitive programs.
Data races remain bugs even under a bounded memory model. The runtime’s synchronization also has a cost. A static guarantee could prevent races and potentially avoid some checks, while keeping the language safe.
The design goal is safe, convenient, predictable control over performance-critical behavior. Preserve type and memory safety and OCaml’s high-level style, but let programmers express the behavior they need rather than depend entirely on optimization heuristics that can change with small code edits.
Most developers do not need to tune every nanosecond, so the added complexity should be paid only when a feature is used. The team also wants compatibility with upstream OCaml and public libraries, with the hope of contributing features upstream.
Three features are central: narrow and flat data layouts, stack allocation, and race-free parallel programming. The team uses ideas from type systems called kinds and modes to express the necessary properties.
Unboxed primitives such as int32#, int8#, and float# can use representations matching their underlying bits rather than the classic boxed or tagged word. They can be placed in records so fields can be represented compactly.

An unboxed record presents a record abstraction to the programmer while its components can be passed separately, for example in registers, rather than through a heap pointer. Arrays of unboxed records can place fields adjacent in memory instead of storing an array of pointers.
This raises a question: the garbage collector and polymorphic code previously relied on uniform values. The proposed type system tracks the representation, called a layout, so the compiler and collector know how to handle different shapes.
A kind can be thought of as a classification for types, just as types classify values. Layouts include the classic value representation, bit widths, floating-point layouts, and void for values that do not need a runtime representation.

An any layout represents a type whose specific layout is unknown. Code using it cannot simply be compiled without more information, but it can be useful in a shared signature whose individual implementations specialize to concrete layouts. Aggregate layouts describe combinations of fields.
For polymorphic functions the compiler can make a copy per layout rather than per individual type. The collector also needs new metadata: a mixed record’s header can identify which fields it must scan and which unboxed fields are not pointers.
Safe stack allocation with modes
Stack allocation can reduce garbage-collection pressure but requires values not to escape. Local and global modes express this constraint and help resource management.
The second feature is stack allocation. Heap allocation in OCaml is efficient, especially in the minor heap, but collection and cache locality still create costs. A larger minor heap needs collection less often but may no longer fit as well in cache.
Stack allocation also advances a pointer, but returning from a function can reclaim the space by moving the stack pointer back without scanning each value. Reusing stack space can touch fewer cache lines. The difficulty is ensuring that a reference never outlives the allocated value.

A classic C use-after-free occurs when a pointer to a stack value is stored and used after the function returns. Safe stack allocation needs a stack discipline: do not create heap references to the local value or return it past its lifetime.
Why not simply use Rust-style lifetimes? Casinghino says fully tracking arbitrary lifetimes can require annotations and complex higher-order reasoning, conflicting with the goal of convenient inference. OCaml can instead put values with complicated lifetimes on the garbage-collected heap.
Modes provide properties orthogonal to the type. Unlike layouts, which describe representation, a mode tracks how a value may be used, including deep properties of nested values. The first distinction is between global and local.
Global is the legacy default, with no constraint on lifetime. Local means the value respects stack discipline and cannot escape by being stored on the heap or returned beyond its scope. The type system tracks usage to determine whether that guarantee holds.
There is a submode relationship: a global value can safely be used where a local value is expected, because doing so imposes more restrictions on its use. This lets existing long-lived values participate in functions that require local behavior.

Consider map: it calls its function argument but does not store or return that function. Marking the function argument local expresses this. A caller such as multiply_by can then create a closure for the multiplier on the stack rather than the heap, avoiding a garbage-collector allocation.

Locality also helps correctness beyond stack allocation. A with_file function opens a file, calls a supplied function with the channel, and closes it afterward. If that callback saved the channel for later use, it could expose a closed handle; a local constraint prevents the channel from escaping.
Data-race freedom and rollout
Contention and portability modes support safe thread APIs and shared memory. Casinghino closes with the status of production use and early rollout.
Modes can also support data-race freedom by tracking operations on values, including aliasing and cross-thread sharing. The first additional axis is contention: an uncontended value is local to a thread, while a contended value has been shared.

The type system can prohibit reading or writing mutable parts of a contended value. That blocks an obvious source of data races, though a practical system must also provide controlled ways to mutate shared state safely.
Portability asks a different question: whether a value may be sent to another thread. A function that captures mutable state it can modify is not portable, because sending that closure would also send the capability to mutate another thread’s state.

The system has several mode axes. Casinghino distinguishes properties of what has already happened, such as whether a value has been shared or aliased, from what may happen later, such as whether it may escape or be shared. Uniqueness and linearity are other axes he mentions.
A thread-spawning API can require a portable function, preventing the unsafe closure case. A join operation waits for the other thread to finish and returns its value; because that thread is no longer running, the returned data need not remain contended merely because it crossed a thread boundary.
For safe mutable shared memory, he sketches an API using locks. A type parameter connects a shared cell to a particular lock, and operations require the matching key. Keys cannot be sent to another thread, so holding the key supports exclusive access while manipulating the protected data.

At the time of the 2025 talk, stack allocation and unboxed types were already being used in production, though more work remained. The data-race-freedom implementation was finished according to Casinghino, but was in the early stages of rollout with teams testing the API.
He reports that the features help performance-focused developers write more idiomatic OCaml with fewer workarounds. The team is also annotating libraries for the new modes and watching whether the added complexity remains manageable for users who do not need those features.
Más de este canal
15:33EN
1:48:29EN
1:36:37EN
1:05:45EN
1:25:00EN
1:16:29EN