The Relaxed Memory Model Zoo

Mapping the space of weak memory models

August 15, 2026

There are today around a hundred published memory models. They span 47 years, from Lamport’s sequential consistency in 1979 to the models published this year; they cover CPUs, GPUs, languages, and persistent and transactional memory; and they were written by communities that cite each other unevenly. Trying to compare models — is Weakestmo weaker than C11, or incomparable to it? — means reading several papers and reconstructing a containment argument that none of them states.

Many publications on verification in a relaxed-memory setting claim genericity by parameterising their algorithms by the memory model, only to then assume that the model is specifiable in cat [1], or that it forbids specific shapes such as thin-air cycles. Finding out which weak memory models are admitted or excluded by such an assumption then requires either a broad literature search or digging through repositories of weak memory model specifications [2].

I wrote the Relaxed Memory Model Zoothe Zoo from now on — as a living survey document which catalogues weak memory models with their properties and the relations between them, and also makes that catalogue accessible and searchable for weak memory researchers. Every model and every edge carries provenance information naming the original publication. Where a relation rests on litmus evidence, the tests that support it ship with it. A time slider makes the chronological development of the field explorable, opening the area up to historians of computing as well. The dataset behind the graph is machine-readable, which makes targeted exploration of weak memory behaviours with algorithmic implementations possible. The screenshot below is taken from the Zoo at rmm-zoo.kissig.org.

A detail of the Zoo's map: rows of memory-model nodes arranged in horizontal bands from Strongest (SC) at the top, through strong relaxations, the thin-air-free tiers holding MRD and sMRD, and down to the language and mid-tier hardware band containing C11, ARMv8, RISC-V and their variants. Arrows descend from stronger to weaker models, with dashed and dotted edges for equivalence, compilation and incomparability.
Excerpt of the Zoo. [fig1]

Execution is not in the order you wrote

In a weak memory setting, memory accesses can become observably out of order: another thread can see them take effect in an order contradicting the order the program is written in. On machines that are not multi-copy atomic, it goes further still: two observer threads can disagree with each other about the order of the same two writes.

Reordering is the common case but not the only one. Eliminating a redundant access, forwarding a value into a register, and — at the pathological end — producing a value with no origin at all belong to the same family: things an implementation may do to your program that its text does not obviously permit. The example below shows how gcc 15.2 -O2 on x86-64 eliminates the first of two ordinary stores to the same variable:

what you wrote
1
2
x = 1;
x = 2;
what gcc -O2 emits
1
movl    $2, x(%rip)

Which modifications an implementation may make is neither arbitrary nor a matter of cleverness — it is written down, in a memory consistency model.

The contract that binds a program to its execution

A memory consistency model is the contract that binds a program to its execution. Adve and Gharachorloo call it “an interface between the programmer and the system”, one that needs a specification at every level of that interface — machine code as much as high-level language [3]. The contract reading is Adve and Hill’s: they re-define weak ordering as “a contract between software and hardware”, in which software agrees to formally specified constraints and hardware agrees to appear sequentially consistent to at least the software that obeys them [4].

The contract is between three parties: it tells the programmer what they may assume about what other threads can observe, the compiler which transformations are sound with respect to the desired target, and the hardware which reorderings and buffering it may perform.

A memory model at the centre, connected to three signatories: the programmer, granted assumptions about what other threads can observe; the compiler, constrained in which optimisations are sound; and the hardware, constrained in which reorderings and buffering it may perform.
The three signatories to a memory model. [fig2]

Each party’s freedom is another’s obligation, which is why most of the Zoo’s property columns describe transformations — whether a reordering, an elimination or a value-forwarding step is sound under the model. A compiler writer reads such a column as the optimisations they may apply, a hardware architect as the reorderings the machine may perform, and a programmer as a guarantee they no longer have. The rest state guarantees outright: coherence, external data-race freedom, absence of undefined behaviour, multi-copy atomicity.

A model fixes the class of behaviours a program may be observed to have, not the behaviour it has. Almost every interesting concurrent program has many permitted outcomes, and the model draws the boundary of that set. This is why the natural ordering on models is set inclusion over behaviours, and why the unit of evidence is a single outcome that falls inside one boundary and outside another.

The contract is drawn at more than one boundary. A hardware model is the contract between an ISA and its microarchitecture; a language model is the contract between a language and its whole implementation — compiler, runtime and hardware together. These are different models, not two statements of one: RC11 says nothing about ARMv8’s store buffers, and ARMv8 says nothing about what a C++ compiler may do to a non-atomic access. What a running program actually gets is the composition of the two, and they compose only when someone proves the compiler’s mapping from one to the other sound. That proof is a relation between models rather than a part of either — which is why it appears in the Zoo as its own edge type: 25 compilation edges, and why, for instance, RC11 reaches hardware through IMM [5] rather than directly.

Not every node on the map is a contract someone signed. SC is a reference point nobody implements; causal consistency and the session guarantees come from distributed storage; snapshot isolation and its persistent variants come from databases. They earn their place by the same test the contracts do: each fixes a set of allowed behaviours precisely enough to be compared by inclusion, and the models that are contracts are positioned against them. That is the criterion for what belongs in the Zoo — anything whose allowed behaviours can be stated sharply enough to be ordered, with a published source to attribute it to.

One program, three permissions

Follow one program through all three. On the left, three statements with ordinary (non-atomic) accesses, in the order you wrote them. On the right, what actually gets executed — real gcc 15.2 -O2 output for x86-64, with directives and address setup elided:

what you wrote
1
2
3
x = 1;
int a = x;
y = a;
what gcc -O2 emits
1
2
movl    $1, x(%rip)
movl    $1, y(%rip)

The load is gone. The compiler forwarded the stored value into the third statement, so the store to y no longer takes its value from x at all, and the dependency that ran load → store in your source no longer exists in the object code. Clang 20 does the same thing on AArch64: both str instructions take their value from the same register, and nothing separates them.

That transformation is the compiler exercising its half of the contract, and the language model is what says it may. Notably, it is also free not to: make those same accesses relaxed atomics and neither GCC 15 nor clang 20 forwards, even though C11 permits it. Implementations are frequently stricter than the contract requires — which is why a model’s guarantees, not any compiler’s behaviour, are what you have to reason against.

Now the third signatory. After forwarding, the object code is two independent stores, and whether another core can observe y == 1 while x is still 0 is no longer the compiler’s business — it is the hardware model’s. On x86-TSO it cannot: stores are not reordered with stores, so the order you wrote survives by accident. On ARMv8 or POWER, where store→store is relaxed, the second store may become visible first, and getting your order back costs an explicit barrier.

Two panels showing the same two stores leaving core P0 through a store buffer on their way to shared memory, with core P1 reading y and then x. Under x86-TSO the buffer drains in program order, so x = 1 reaches memory before y = 1 and a reader that sees y = 1 must see x = 1. Under ARMv8 or POWER the two stores are unordered in the buffer, so y = 1 can reach memory while x is still 0 and P1 reads y = 1 with x = 0.
The same two stores, under two hardware models. The buffer between the core and memory is where the order is kept or lost. [fig3]

That single question — may store→store be reordered — is one of the 28 property columns (reorder_ss), and the reason TSO, PSO and ARMv8 are three different nodes on the map rather than one. What another thread finally observes is the composition of two permissions granted by two different documents: one the compiler took under the language model, one the CPU took under its own. Neither document alone predicts it.

Weak, relative to what

The word doing the work in weak memory model is a comparison to sequential consistency [6], in which every execution is some interleaving of the threads’ program orders. SC is the model nearly everyone reasons with informally, and nearly nobody implements: store buffers, caches, speculation and ordinary compiler optimisations all violate it. Every model below SC in the map keeps part of that intuition and gives up the rest. What separates them is where they draw the line — which guarantees a programmer loses so that a store buffer, a cache or an optimisation can be kept.

Language models mostly recover the intuition conditionally rather than abandoning it: if your program is data-race-free, you are promised sequential consistency. What happens when it is not race-free is where the contracts genuinely diverge — undefined behaviour in C++ [7][8], a bounded safety guarantee in Java [9], races bounded in space and time in OCaml [10]. Two of the property columns, no_ub and edrf, track exactly this split.

For some of the mainstream language models, this contract is known to be defective. The C11/C++20 axioms fail to forbid out-of-thin-air values that no compiler and no CPU actually produce. A substantial share of the models in the Zoo exist to close that gap, and the disagreements between them are still live — the subject of its own section below. The map is, in that sense, a picture of a contract still under negotiation — not a settled taxonomy.

Wherever a model draws its line, a litmus test pins down one point on it: a small program with an outcome reachable only if a specific relaxation is allowed.

What a litmus test looks like

A litmus test is a tiny concurrent program — usually two to four threads, a handful of accesses — together with an initial state and a question about the final state. It is not a test in the software-engineering sense. It asks whether a particular outcome is permitted, and the answer is a property of the model, not of any one run.

Here is the one the SC → TSO edge rests on, exactly as it sits in the repository:

1
2
3
4
5
6
7
X86 SB
"Store buffering: store->load reordering. Forbidden under SC, allowed under TSO."
{ x=0; y=0; }
 P0          | P1          ;
 MOV [x],$1  | MOV [y],$1  ;
 MOV EAX,[y] | MOV EAX,[x] ;
exists (0:EAX=0 /\ 1:EAX=0)

Four parts: the architecture and a name; the initial state (x and y both zero); two threads, each storing to one location and then loading the other; and the condition — the outcome we are asking about. Here it asks whether both threads can read 0.

For both loads to return 0, each thread’s load must effectively happen before the other thread’s store — which requires the store to be delayed past the load. That is store→load reordering, and whether it is allowed is precisely what separates sequential consistency from TSO. Run the same file through herd7 [1] under the two models:

1
2
3
4
5
$ herd7 -model sc.cat     SB.litmus
Observation SB Never 0 3

$ herd7 -model x86tso.cat SB.litmus
Observation SB Sometimes 1 3

Never 0 3 means the tool enumerated three candidate executions and none satisfied the condition — the outcome is forbidden. Sometimes 1 3 means one of three did — it is allowed. A store buffer, which lets a store sit in a per-core queue while later loads proceed, is exactly the implementation that makes the second verdict true.

The same discipline extends past herd7 where it has to: the Java and OCaml edges run under a JDK and an OCaml runtime, the thin-air edges run under MoRDor [11]. A follow-up post will show how the same tests locate a real language implementation in the lattice, using Rust as the example and running under loom, Miri and real silicon.

One test, run under two models, yields two verdicts — and that pair is the witness for a relation in the graph.

The ordering, and what an edge means

Models are ordered by the inclusion of the behaviours they allow. A → B strictly weaker means every execution A permits, B permits too, and there is at least one B permits that A forbids — the arrow runs from the stronger model to the weaker one. The current dataset contains 59 strictly-weaker edges, 26 incomparable pairs, 35 equivalences and 25 compilation mappings, with 112 references spanning 1979–2026.

In the Zoo, that separation is witnessed by a test shipped with the dataset wherever one exists. Take the store→store question the compiler example ended on, as the Zoo’s TSO → PSO edge states it, in the message-passing shape:

1
2
3
4
5
6
7
8
9
AArch64 MP
"Message passing with two plain stores. Store->store reordering."
{ 0:X1=x; 0:X3=y; 1:X1=y; 1:X3=x; }
 P0           | P1           ;
 MOV W0,#1    | LDR W0,[X1]  ;
 STR W0,[X1]  | LDR W2,[X3]  ;
 MOV W2,#1    |              ;
 STR W2,[X3]  |              ;
exists (1:X0=1 /\ 1:X2=0)

P0 writes the data x and then the flag y; P1 reads the flag and then the data. The condition asks whether P1 can see the flag set (1:X0=1) while still reading the data as stale (1:X2=0) — the message arriving before its contents. Run the same file under both models:

1
2
3
4
5
$ herd7 -model abstract-tso.cat MP.litmus
Observation MP Never 0 3

$ herd7 -model abstract-pso.cat MP.litmus
Observation MP Sometimes 1 3

Forbidden under TSO, allowed under PSO. Without such a program the claim would be an assertion about two papers rather than a fact about two models.

A → B asserts two things: containment, that every behaviour A allows B allows too, and strictness, that B allows at least one thing A does not. A litmus test is an existential — it settles strictness. Containment is universally quantified over all programs, and no finite number of tests establishes it. The dataset therefore takes strictness from a test where one exists, and containment from a reading of the literature — provisionally, until a converse witness establishes otherwise. It is partial by construction, and a work in progress.

A pair with a witness in one direction and none in the other is drawn as strictly weaker; the same pair would be incomparable if a converse witness turned up. Not finding a separating program is the current state of a search, not a proof that none exists.

What may an implementation do under this model?

In addition to answering “is A weaker than B?”, the Zoo answers “what may an implementation do under A?”. So every model also carries a property vector: 28 boolean cells in seven groups, following the framing of the survey [12], where a model is characterised by what it permits an implementation to do — which reorderings, which eliminations, which transformations survive it.

Group Cells Asks
Compilation 4 is the optimal mapping to x86 / POWER / Armv7 / Armv8 sound?
Reordering 4 may adjacent accesses be reordered — Store→Load, Store→Store, Load→Load, Load→Store?
Elimination 4 may a redundant Store/Load, Store/Store, Load/Load or Load/Store pair be eliminated?
Other local 7 irrelevant load elimination, speculative load introduction, roach motel and its inverse, strengthening, trace preservation, common subexpression elimination
Global 3 register promotion, thread inlining, value-range reasoning
Reasoning 5 external DRF, coherence, no undefined behaviour, in-order execution, no out-of-thin-air
Atomicity 1 multi-copy atomicity

Read across a row and you get a model’s profile; read down a column and you get a map of the field on one question. Some of those columns are more interesting than the lattice: 73 of 101 models are recorded as forbidding out-of-thin-air, 31 as multi-copy-atomic, and 28 as offering an external-DRF guarantee — the property most directly relevant to a programmer who wants to reason about race-free code without reasoning about the model at all. Those are counts of what the dataset records, not of what is true of the field: no column is complete, and external DRF is the extreme case — 28 yes, 6 no, and two thirds of the models unrecorded. An empty cell means the question has no answer yet, not that the answer is no.

Model properties are taken from a literature survey wherever the survey lists them: 29 of the 101 rows come straight from its tables [12]. The survey covers programming-language models, so most hardware and GPU models are outside it, and their rows are author-extrapolated — the other 72. That means the value was worked out from the model’s own definition rather than copied from a source that states it: reading the defining paper and deciding whether, say, its axioms permit store→store reordering — a judgement call rather than a quotation.

Provenance is recorded per row, as it is per edge, and the UI flags which of the two a model’s vector is. Where a cell rests on a specific result, it carries its own citation: the inverse-roach-motel column, for instance, is assigned based on the strength of Poetzl and Kroening’s soundness proof for the SC-for-DRF execution model [13], and each such cell references the result.

Out of thin air

A model that does not constrain reordering tightly enough admits behaviours no implementation ever produces. The sharpest case is a value appearing that nothing in the program ever wrote. Batty et al.’s formalisation of C++11 [8] exposed it in the mainstream language models: the standard’s prohibition on out-of-thin-air reads was informal prose that resisted formalisation, so the model as written does not actually forbid such executions — a gap they later set out in full [14]. RC11 and the models after it exist to close it.

Start from the compiler example above. The compiler kept 1 in a register and reused it, deleting the load — and with it, the dependency that ran from the load to the store. That is an ordinary, desirable optimisation. It is also the root of the trouble: a syntactic dependency in your source is not a fact about the program’s semantics, and implementations are free to discover that it carries no information and remove it. Register caching and value forwarding are the plain cases; common-subexpression elimination and value-range reasoning do the same thing less visibly.

That freedom has a consequence for the specification. A model cannot simply decree “respect dependencies”, because it cannot say which ones are real without either forbidding those optimisations or reasoning about the semantics of the whole thread. The axiomatic C11/C++20 models therefore validate an execution by a weaker local condition — every read reads from some write — and that condition can be satisfied by a cycle that supports itself:

Two threads. P0 reads x and, only if it read 42, writes 42 to y. P1 reads y and, only if it read 42, writes 42 to x. Each read takes its value from the other thread's guarded write, so the two justify each other in a cycle, and no instruction ever stores 42 unconditionally.
Where the thin-air value comes from: nowhere. Each read is justified by the other thread's write, and each write is guarded by that read. [fig4]

Both threads read 42, and both write 42 only because they read it. No instruction anywhere stores 42 unconditionally — the constant does not occur in the program except as the thing being tested for. Yet nothing in the C11 axioms rules the execution out. The value is invented out of thin air, and the literature’s name for the phenomenon is exactly that [15].

No compiler and no processor actually does this, so the specification is strictly weaker than every implementation of it. What it costs is formal reasoning: with thin-air values permitted, you cannot prove that a value your program never wrote cannot appear — fatal for security arguments and for compositional verification, and forbidding it turns out to cost real performance [16].

The models that close the gap are among the most interesting recent ones, and they disagree about how: the acyclic(po ∪ rf) axiom in RC11 [17], promised writes that a thread later certifies in Promising [18], alternative executions tracked to tell a real dependency from a false one in the event-structure models of Jeffrey and Riely [19] and Weakestmo [20], and modularly computed dependencies in MRD [21]. MRD is the one the Zoo’s thin-air edges are actually checked against, since herd7 cannot construct such an execution at all.

So the most important open question in the field is precisely the one the standard tool cannot exhibit.

Where the data comes from, and what I trust it for

The models, the relations between them and the property values come from three sources: my own notes from working in the field, the survey of programming-language memory models by Moiseenko, Podkopaev and Koznov [12], and LLM-based summarisation of publications in the field. What happens to them afterwards is a pipeline with three parts:

Extraction. Large language models did the reading-at-scale: summarising papers, surfacing candidate models worth including, pulling property values out of the survey’s tables, and collecting the bibliographic details that became the references block. A hundred models across 47 years of proceedings is more than I could read.

Presentation. The web interface that renders all this — the map, its filters, the per-model pages — is written and deployed separately from the dataset.

Support. Every ordering claim in the published dataset is carried by something a reader can check independently: a litmus test in the repository that runs and produces the verdict, a construction argument, or a citation to the primary source that settles it. That is what the provenance and evidence labels record, per edge.

Extraction and support are separated because support is what confirms extraction. They also run on different clocks: a model, an edge or a property cell is extracted once, while the support behind it is re-checked on every commit. A litmus test that contradicts its recorded verdict fails CI, an edge that contradicts the transitive closure of the others fails the gate before it can ship, the per-edge evidence label keeps a cited edge legible as cited rather than blending into the machine-checked ones, and the structural checks catch an edge that may be accepted in a review but does not hold.

This invites the obvious question: how much of the map is supported by evidence?

Evidence, and its absence

The Zoo is worth only as much as its provenance discipline.

Every edge carries two orthogonal labels. Provenance says where the claim comes from: a litmus test in the repository (11 edges), a memalloy model-comparison result (4), or the literature (130). Evidence says what kind of thing backs it: machine_run — a tool in the repository actually produces the verdict (40 edges); by_construction (19); or cited (86).

Only 40 out of 145 edges are currently machine-run. The reasons for the gap are:

  • herd7 cannot exhibit out-of-thin-air executions, as the thin-air section ended on. For the thin-air edges — MRD → C11 and its counterparts from sMRD, Weakestmo and CSRA — the strong side is checked in MoRDor [11] where possible, and the C11 permission is a property of the standard’s axioms, cited rather than exhibited.
  • Several models ship no cat model at all. IMM and Promising have their own artifacts (a Coq development, a dedicated tool); the survey’s research models — BMM, RMMOA, CRC, JAM, OHMM, WJES, GOS, JSMM, RMC, RAO, TSC — have none. Where a model coincides with a bundled one, it is checked through it (BMM ≡ TSO, RMMOA ≡ PSO, both fully machine-run). Otherwise the test ships with a documented verdict cited to the model’s own artifact.
  • Some separations are not single litmus outcomes. External-DRF reasoning guarantees and transformation-soundness differences do not reduce to one program’s outcome, and those pairs’ READMEs say so explicitly.

What is machine-run is run continuously: 65 herd7 checks, each asserting a specific Never or Sometimes verdict, executed on every push and pull request against a pinned herdtools7 7.58. A verdict that changes, because of a tool upgrade for instance, fails CI.

Around that sits a consistency gate of eight checks, run before any build or deploy. Six of them guard the data: strictly-weaker is a DAG; no pair is both ordered and incomparable including through the transitive closure, so a deduced order cannot contradict a drawn incomparable edge; every witness directory matches its edge’s type and direction; and a pair exercised on both sides shows a genuine Never + Sometimes split rather than two identical verdicts. The other two guard the visual drawing of the map.

Applying the gate reclassified three edges originally drawn as incomparable, after sweeps failed to produce the witness incomparability implies: PSO ↔ POWER (every PSO-allowed behaviour I could find is POWER-allowed, while POWER allows load-load reordering PSO forbids), Weakestmo ↔ C11 and CSRA ↔ C11 (both are thin-air-free repairs of C11, so they allow strictly less than it does). All three are now strictly-weaker edges, and the history of the correction is in the repository rather than silently overwritten.

As a research tool: finding models

I wrote the Zoo to make it possible to search over a space that is otherwise distributed across proceedings. The map filters by the language a model targets, by formalism style, by year, and by the property vectors from Moiseenko, Podkopaev and Koznov’s survey [12] — so “which models are thin-air-free, multi-copy-atomic, and support store→load reordering?” becomes a query rather than a literature review.

Cat specifiability matters for anyone mechanising. cat is the domain-specific language in which herd7 models are written [1] — a model is a handful of definitions over the standard relations (po, rf, co, fr, dependencies, fences) plus a few acyclicity or irreflexivity axioms over them, and herd7 turns that description directly into an executable checker. It is the closest thing the field has to a common notation. Each model is therefore classified as specified (a real cat model exists — 25 models), expressible (it could be written in cat, but nobody has — 62), or not-expressible (it cannot be stated in cat at all — 14, typically the operational and event-structure models, whose content is a stepwise construction rather than a predicate over a candidate execution). That column is a map of where the tooling stops, which is often exactly what you want to know before starting a mechanisation.

It also answers a question that comes up constantly on the verification side. A whole line of work is deliberately parameterised by the memory model, because that is how it buys generality. Instead of building on one model, these publications state their results for a class of models with assumed properties. The Zoo turns “does this algorithm apply to my model?” into a lookup. If the model is specified, the input the algorithm wants already exists. If it is expressible, the algorithm applies in principle and someone has to write the cat file first. If it is not-expressible, no amount of engineering will make that class of technique fit, and you need a different one. Read that way, the 25 / 62 / 14 split is a statement about the reach of an entire family of verification algorithms: 87 of the 101 models are within it, and 14 are structurally outside.

As an instrument: locating a real implementation

Everything so far relates published models to published models. One use case turns the dataset the other way round, and points it at a shipped implementation.

The litmus tests are not only evidence for the edges — they are a reusable instrument. Each one is a program whose outcome separates two named models, with the expected verdict on both sides written down. Port that program to a real language and run it, and the verdicts tell you which side of the edge the implementation falls on. The catalogue stops being a map of the literature and becomes a way of locating something real within the literature.

Rust is a good subject, because the question is genuinely open: it ships no Rust.cat, and core::sync::atomic is documented to follow the C/C++ atomic memory model, which is a statement of intent rather than a position in a lattice. In the follow-up post I will write about how I used the Zoo’s language-level tests on Rust atomics and probed the boundaries of loom, Miri, herd7 and real silicon. Spoiler: they did not agree on all behaviours.

The part worth borrowing is not Rust’s position relative to the Zoo but the method. Nothing about the procedure is Rust-specific: a lattice whose edges are defined by runnable tests turns “where does this implementation sit?” from an essay question into a finite, executable checklist, and any language with atomics and a checker to run them under can be located the same way.

Guiding principles

I could have made the Zoo much bigger. Every survey or article I read named models I had not included, every paper offered another property worth tabulating, and once a language model is doing the reading for you, collecting all of it is nearly free. What stopped me was knowing the mistakes I make, and having watched the ones the models make. Both kinds are silent and hard to catch.

The question I kept returning to was not so much how many models I could collect, but how I could be certain of a relation between models or a property of a model. A litmus test gives me an existential — this model allows what that one forbids — and nothing beyond it, and no framework describes all hundred models in one vocabulary. The dataset was going to be partial whatever I did; the only real choice was whether it would say so.

The concession I find hardest is the one about containment. When I draw an edge, one direction is witnessed and the other is read out of the literature: the pair stays strictly weaker only until someone finds a program separating it the other way, at which point it is incomparable. I would rather read containment as a partial judgement than infer a total one from evidence that does not exist.

Three principles fall out of these questions:

Evidence over assertion. Every claim is supported by evidence: an edge records how it is known and a property cell records where its value came from. The label — machine_run, cited, by_construction, survey-sourced, author-extrapolated — is part of the dataset.

Witnessed separation over assumed containment. A litmus test can only establish that one model allows what another forbids. The containing direction comes from a reading of the literature and stands provisionally, until a converse witness says otherwise.

Trust over completeness. An empty cell is an open question, not a no. Unknown values stay unknown, and every count is a count of what the dataset records rather than of what is true of the field.

Contributing, and reporting errors

I created the Zoo as a community documentation project, where facts are collected, structured, cross-checked and made publicly available. A contribution is a missing or incorrect model, property or relation, supported by a paper with a DOI, a specification, a tool artifact, or ideally executable tests.

The dataset is hosted on GitHub: rmm-zoo-datasetmodels.json, the litmus witnesses, and the gate that guards them. Every claim in this post is a line in that repository, and that is where contributions go. The site that renders the data is a separate project that only consumes the published dataset, and a new dataset version reaches it only from a tagged release.

If you see anything wrong or missing, please report it here:

  • Open an issue for anything you believe is wrong — a misclassified edge, a wrong year or attribution, a property cell that is not established by its primary source. An issue is also the right move for a model you think belongs but are unsure how to position.
  • Open a pull request using the template for the kind of change: adding a model, correcting a model, correcting a relation, adding property columns, or adding litmus tests. Each template lists the data files that change together, the invariants that apply, and the regeneration steps.
  • CONTRIBUTING.md covers the dataset’s structure, the provenance and evidence vocabularies, and the checks that guard the data.
  • CI runs the litmus suite and the consistency gate on every push and pull request, so a change that breaks an invariant tells you before review does.

The map is live at rmm-zoo.kissig.org. If the Zoo proves useful for the community, it will get its own domain.

Conclusions

Today, over a hundred memory models exist, and almost none of the relations between them are written down anywhere. The Zoo is my attempt to collect them in one place, under a common taxonomy of relations and properties: 101 models, 145 relations, and a record on each of how it is known.

The guiding choice was trust over completeness, and that makes the Zoo partial by construction. Most relations say less than they look like they say — one direction is witnessed by a program, the other read out of the literature and standing only until someone finds the converse. Where a tool could produce the verdict, I took that over a reading of a paper: 40 of the 145 edges are machine-run, and the rest state that they are not.

Compiling the dataset has taught me about the individual models, and even more about the methodology for describing and comparing them. I hope the Zoo proves useful for the wider community as a research tool, and is taken further as a living document.

Thanks

I would like to thank Mark Batty and Dominic Orchard for discussions — and their initial scepticism. The idea of the Zoo is borrowed from the original Complexity Zoo.

References

  1. Alglave, Maranget, Tautschnig: Herding Cats: Modelling, Simulation, Testing, and Data Mining for Weak Memory, ACM TOPLAS, 2014 10.1145/2627752
  2. Alglave, Maranget et al.: herdtools7 — the herd7 distribution and its bundled catalogue of cat models github.com/herd/herdtools7
  3. Adve, Gharachorloo: Shared Memory Consistency Models: A Tutorial, IEEE Computer 29(12), 1996 10.1109/2.546611
  4. Adve, Hill: Weak Ordering — A New Definition, ISCA 1990 10.1145/325096.325100
  5. Podkopaev, Lahav, Vafeiadis: Bridging the Gap between Programming Languages and Hardware Weak Memory Models, POPL 2019 10.1145/3290382
  6. Lamport: How to Make a Multiprocessor Computer That Correctly Executes Multiprocess Programs, IEEE Transactions on Computers, 1979 10.1109/TC.1979.1675439
  7. Boehm, Adve: Foundations of the C++ Concurrency Memory Model, PLDI 2008 10.1145/1375581.1375591
  8. Batty, Owens, Sarkar, Sewell, Weber: Mathematizing C++ Concurrency, POPL 2011 10.1145/1926385.1926394
  9. Manson, Pugh, Adve: The Java Memory Model, POPL 2005 10.1145/1040305.1040336
  10. Dolan, Sivaramakrishnan, Madhavapeddy: Bounding Data Races in Space and Time, PLDI 2018 10.1145/3192366.3192421
  11. Kissig: MoRDor — a tool for calculating the weak memory semantics of C programs github.com/christiankissig/mordor
  12. Moiseenko, Podkopaev, Koznov: A Survey of Programming Language Memory Models, Programming and Computer Software 47(6), pp. 439–456, 2021 10.1134/S0361768821060050
  13. Poetzl, Kroening: Formalizing and Checking Thread Refinement for Data-Race-Free Execution Models, arXiv:1510.07171, 2015 1510.07171
  14. Batty, Memarian, Nienhuis, Pichon-Pharabod, Sewell: The Problem of Programming Language Concurrency Semantics, ESOP 2015 10.1007/978-3-662-46669-8_12
  15. Boehm, Demsky: Outlawing Ghosts: Avoiding Out-of-Thin-Air Results, MSPC 2014 10.1145/2618128.2618134
  16. Ou, Demsky: Towards Understanding the Costs of Avoiding Out-of-Thin-Air Results, PACMPL 2 (OOPSLA), Article 136, 2018 10.1145/3276506
  17. Lahav, Vafeiadis, Kang, Hur, Dreyer: Repairing Sequential Consistency in C/C++11, PLDI 2017 10.1145/3062341.3062352
  18. Kang, Hur, Lahav, Vafeiadis, Dreyer: A Promising Semantics for Relaxed-Memory Concurrency, POPL 2017 10.1145/3009837.3009850
  19. Jeffrey, Riely: On Thin Air Reads: Towards an Event Structures Model of Relaxed Memory, LICS 2016 10.1145/2933575.2934536
  20. Chakraborty, Vafeiadis: Grounding Thin-Air Reads with Event Structures, POPL 2019 10.1145/3290383
  21. Paviotti, Cooksey, Paradis, Wright, Owens, Batty: Modular Relaxed Dependencies in Weak Memory Concurrency, ESOP 2020 10.1007/978-3-030-44914-8_22
×

Cite this post

Copied to clipboard!