Why languages need memory models too
Everything so far — chapters 3, 4, and 5 — has defined memory models at the hardware/low-level-software interface: what software authors may expect, what hardware implementors may do. The same two questions arise one level up: a high-level language memory model hll memory model A high-level language's memory contract (Java, C++), built on SC for DRF with atomic/synchronized tagging plus bounded semantics for racy programs. defined in Chapter 5 — open in glossary specifies (a) what HLL software authors may expect and (b) what implementors of compilers, runtime systems, and hardware may do:
Figure 5.6 (recreated): (a) high-level language and (b) hardware memory models.
Many HLLs were born in a largely single-threaded world and simply omitted a memory model — there isn’t one in Kernighan and Ritchie. Java was perhaps the first mainstream language with a memory model, and its first version had real issues (Pugh: “fatally flawed”). Memory models were then re-specified for Java (2005) and specified for C++, and the cornerstone of both is a familiar one — SC for DRF, provided in part by Sarita Adve’s co-authorship of all three papers.
SC for DRF at the language level
To allow synchronization races but not data races, programmers tag
variables that might race as synchronization — the atomic keyword, or
implicit locks via Java’s monitor-like synchronized methods.
Implementations are then free to reorder or even eliminate memory
accesses between synchronization accesses. Figure 5.7’s example:
(a) High-level language
B = 2*A;
D = C - A;(b) Naive compilation
L1: r1 = A;
X1: r2 = 2*r1;
S1: B = r2;
L2: r3 = C;
L3: r1 = A;
X2: r2 = r3-r1;
S2: D = r2;Memory order: L1, S1, L2, L3, S2
(c) A allocated to register r1
L1,L3: r1 = A;
X1: r2 = 2*r1;
S1: B = r2;
L2: r3 = C;
/* L3 moved */
X2: r2 = r3-r1;
S2: D = r2;Memory order: L1+L3, S1, L2, S2 — L3 reordered and MERGED with L1
The reordering is correct because, with SC for DRF, no other thread “can be looking.” And register allocation has plenty of company: constant propagation, common subexpression elimination, loop fission/fusion, loop-invariant code motion, software pipelining, and instruction scheduling all reorder memory accesses. Because SC for DRF allows them, compilers and runtimes produce code with performance comparable to single-threaded code.
When programs race anyway
What if a program has data races data race Two conflicting data operations that appear in the global memory order without an intervening pair of transitively conflicting synchronization operations by the same threads. defined in Chapter 5 — open in glossary — by accident or on purpose? Another thread could then observe a non-SC execution: in Figure 5.7c, a core C2 could update A and C, and C1 — with A parked in r1 — could observe the update to C but not to A. HLL models must handle this, with different motivations:
- Java requires security guarantees for all programs — it can never leave racy behavior undefined.
- C++, for performance, supports low-level atomics — synchronization operations weaker than SC.
So both must specify behavior in all cases, with three goals: (1) allow all optimizations for high-performance DRF programs, (2) unambiguously specify the allowed behavior of all programs, and (3) keep that specification simple. In the book’s judgment, the 2005 Java model made substantial progress — arguably succeeding on (2), mostly succeeding on (1) (it disallowed some compiler optimizations), but not on (3). One “dark corner” every compiler and runtime author must confront: regardless of optimizations, a load must never return a value out of thin air out-of-thin-air A load result with no producing store, arising from speculative load-buffering cycles; forbidden by dependency-induced ordering. defined in Chapter 5 — open in glossary (§5.6.1’s load-buffering example) — and that is not the only complexity. Satisfying all three goals at once remains an open, actively researched problem in C++. Fortunately, most programmers can use SC for DRF and never meet the dark corners.
Must relaxed languages mean relaxed hardware?
Java and C++ adopt the relaxed, SC-for-DRF approach. Should the hardware underneath be relaxed too? On one hand: relaxed hardware maximizes performance, and compilers/runtimes need only translate the HLL’s synchronization into the hardware’s low-level operations and FENCEs. On the other: SC and TSO already perform well, and stronger hardware lets compilers generate more portable code without fences from incomparable models. The debate is not settled, but one thing is clear: relaxed HLL models do not require relaxed hardware.
Check yourself
1.Why do high-level languages need memory models of their own, on top of the hardware's?
2.Figure 5.7 shows register allocation turning 'B = 2*A; D = C − A;' into code where load L3 merges with L1. Why is this a MEMORY MODEL issue, and when is it legal?
3.Both Java and C++ must specify behavior for ALL programs, even racy ones — but for different reasons. Which?
4.How does the book judge the 2005 Java memory model against its three goals?
5.Flashback (pop quiz Q5): 'A programmer who writes properly synchronized code relative to the HLL model (e.g., Java) need not consider the architecture's memory model.' True or false?
6.If HLLs use relaxed (SC-for-DRF-based) models, must the HARDWARE underneath also be relaxed?
Chapter 5 references
- (Don’t) Do It Yourself: Weak Memory Models. diy.inria.fr
- S. V. Adve and K. Gharachorloo. Shared memory consistency models: A tutorial. IEEE Computer, 29(12), 1996.
- S. V. Adve and M. D. Hill. Weak ordering—a new definition. ISCA, 1990.
- S. V. Adve and M. D. Hill. A unified formalization of four shared-memory models. IEEE TPDS, June 1993.
- S. V. Adve, M. D. Hill, B. P. Miller, and R. H. B. Netzer. Detecting data races on weak memory systems. ISCA, 1991.
- J. Alglave, L. Maranget, S. Sarkar, and P. Sewell. Fences in weak memory models. CAV, 2010.
- J. Alglave, L. Maranget, S. Sarkar, and P. Sewell. Litmus: Running tests against hardware. TACAS, 2011.
- J. Alglave, L. Maranget, P. E. McKenney, A. Parri, and A. Stern. Frightening small children and disconcerting grown-ups: Concurrency in the Linux kernel. ASPLOS, 2018.
- ARM. ARM Architecture Reference Manual, ARMv7-A and ARMv7-R Edition Errata Markup, 2011.
- ARM. ARM v7A+R Architectural Reference Manual.
- ARM. ARM v8 Architectural Reference Manual.
- M. Batty, K. Memarian, K. Nienhuis, J. Pichon-Pharabod, and P. Sewell. The problem of programming language concurrency semantics. ESOP, 2015.
- M. Batty, S. Owens, S. Sarkar, P. Sewell, and T. Weber. Mathematizing C++ concurrency. POPL, 2011.
- H.-J. Boehm and S. V. Adve. Foundations of the C++ concurrency memory model. PLDI, 2008.
- H. W. Cain and M. H. Lipasti. Memory ordering: A value-based approach. ISCA, 2004.
- L. Ceze, J. Tuck, P. Montesinos, and J. Torrellas. BulkSC: Bulk enforcement of sequential consistency. ISCA, 2007.
- S. Chakraborty and V. Vafeiadis. Grounding thin-air reads with event structures. POPL, 2019.
- M. Dubois, C. Scheurich, and F. A. Briggs. Memory access buffering in multiprocessors. ISCA, 1986.
- S. Flur et al. Mixed-size concurrency: ARM, POWER, C/C++11, and SC. POPL, 2017.
- K. Gharachorloo, D. Lenoski, J. Laudon, P. Gibbons, A. Gupta, and J. Hennessy. Memory consistency and event ordering in scalable shared-memory. ISCA, 1990.
- C. Gniady, B. Falsafi, and T. Vijaykumar. Is SC + ILP = RC? ISCA, 1999.
- M. D. Hill. Multiprocessors should support simple memory consistency models. IEEE Computer, 31(8), 1998.
- IBM. Power ISA Version 2.06 Revision B, 2010.
- IBM. Book E: Enhanced PowerPC Architecture, Version 0.91, 2001.
- B. W. Kernighan and D. M. Ritchie. The C Programming Language, 2nd ed., Prentice Hall, 1988.
- S. Mador-Haim, R. Alur, and M. M. K. Martin. Generating litmus tests for contrasting memory consistency models. CAV, 2010.
- J. Manson, W. Pugh, and S. V. Adve. The Java memory model. POPL, 2005.
- P. E. McKenney. Is Parallel Programming Hard, And, If So, What Can You Do About It? kernel.org, 2011.
- A. Meixner and D. J. Sorin. Dynamic verification of memory consistency in cache-coherent multithreaded computer architectures. DSN, 2006.
- W. Pugh. The Java memory model is fatally flawed. Concurrency: Practice and Experience, 12(1), 2000.
- The RISC-V Instruction Set Manual, Volume I: Unprivileged ISA.
- J. Ševčík and D. Aspinall. On validity of program transformations in the Java memory model. ECOOP, 2008.
- R. L. Sites, Ed. Alpha Architecture Reference Manual. Digital Press, 1992.
- D. L. Weaver and T. Germond, Eds. SPARC Architecture Manual (Version 9). PTR Prentice Hall, 1994.