OCaml atomics on real hardware

What the compiler and runtime actually deliver, per machine

This page documents and summarises known behaviour of OCaml's memory model as delivered through Atomic on real hardware. It is not a definitive source, it is subject to change, and corrections are welcome. It exists mainly to record behaviour I have observed whilst undertaking native atomics work in the OCaml compiler. The code behind these tests is Témoin.

The tests run against Atomic rather than assembly, so they measure what the compiler and runtime give you, not what the architecture permits.

Not covered. Mutex, Condition, effects, and Domain beyond spawning. Absence from this page means untested, not permitted.

Shapes

TestWhat it checks
MPA value written before an atomic flag is visible to whoever reads the flag
MP publicationThe same without atomics
SBStore buffering, forbidden for atomics
SB controlStore buffering with plain accesses, which hardware may legally do
LBLoad buffering
CoRRHaving seen the new value, you cannot see the old one again
WRCCausality across three domains
IRIWWhether four domains agree on the order of two independent writes
CASExactly one winner
FAATwo increments, two distinct old values
XCHGExactly one domain sees the initial value

MP, SB and LB run again for each storage kind.

KindWhat it is
atomicint Atomic.t
plainPadded int array slot
fieldMutable record field holding an immediate
ptrfieldMutable record field holding a pointer, so caml_modify
floatarrfloat array element, unboxed and flat

Observed

amd64 Power10 SpacemiT X60 Ampere eMAG z15
Architecture x86-64POWERRISC-VAArch64z/Architecture
Cores 121928322
SB control fires 29,387 11 fires does not fire
WRC, IRIW okokokokskipped
Everything else okokokokok

Nothing has produced a forbidden outcome through Atomic on any machine.

A clean run only counts if SB control fires. It is racy plain accesses in a shape no fence orders, so the hardware is free to produce the forbidden outcome, and firing proves the harness can see one. The z15 is ordered strongly enough that it never fires, so that run detects nothing and reports a non-result rather than a pass.

Plain accesses on POWER are not multi-copy atomic, so two readers may disagree about the order of two independent writes. A million iterations of IRIW across four domains on a 192-core Power10 produced no disagreement. An observation, not a guarantee.

float array loses the fence

MP publication holds without atomics because OCaml emits lwsync before every plain store to mutable heap state on POWER, which orders store against store but not store against load. That covers value stores only. A float array element is an unboxed double, so POWER emits lwsync then std for a value store and a bare stfd for a float one.

MP publication Power10 SpacemiT X60
floatarr 443 in 500,000 89 in 20,000,000
plain silentsilent
field silentsilent
ptrfield silentsilent

Not a bug. The fence exists so nobody sees a pointer into a half-initialised block, and float arrays hold no pointers. The trap is that it gives int arrays an ordering that does not generalise, and code leaning on it loses the guarantee the moment the value is a float.

Reproducing

dune build && ./_build/default/bin/main.exe -n 1000000

-only NAME runs a subset. Counts are single runs at the iteration counts given, on the machines named.

Revisions