6.1.5 · D4Parallelism & Multicore

Exercises — Shared memory vs distributed memory

2,086 words9 min readBack to topic

Before we begin, the three numbers we will reuse everywhere (all from the parent note):


Level 1 — Recognition

Exercise 1.1 (L1)

For each system, say shared or distributed memory: (a) A laptop with an 8-core Intel CPU running one program with 8 threads. (b) A 500-node cluster where each node is a separate computer connected by InfiniBand. (c) 100 cloud VMs coordinating via a network.

Recall Solution 1.1

(a) Shared. One physical memory, all 8 cores read/write the same global address space directly. (b) Distributed. Each node has its own private memory; nodes talk by passing messages. (c) Distributed. VMs cannot touch each other's RAM; the only channel is the network.

Rule of thumb: Can core X read core Y's data with a plain load instruction? Yes → shared. Only by sending a message → distributed.

Exercise 1.2 (L1)

In the sentence "Processor A calls send(data, size, dest) and Processor B calls receive()," which memory model is this, and what would the equivalent action look like in the other model?

Recall Solution 1.2

This is distributed memory — communication is explicit (you literally name the operation). In shared memory the equivalent is implicit: A simply does x = 5; and B later does read x. No send/receive appears — the hardware moved the data for you (via the cache-coherence protocol). See Parallel Programming Models.


Level 2 — Application

Exercise 2.1 (L2)

A core has , , and a hit rate of . Compute .

Recall Solution 2.1

. What it means: even though 95% of accesses are fast, the rare 5% of slow misses dominate the average (5.0 of the 5.95 ns). Misses are the enemy.

Exercise 2.2 (L2)

A network has and . Compare sending one message of 1 MB vs 1000 messages of 1 KB each (same total 1 MB). (Use bytes, bytes, bytes.)

Recall Solution 2.2

Transfer time per byte-batch is . With bytes/s:

One big message: bytes.

1000 small messages: each pays its own .

The batched version is faster. Why: the fixed latency is paid per message, so slicing your data into 1000 pieces pays it 1000 times. This is why MPI programs batch — see the parent note. Related: Interconnect Networks.

Figure — Shared memory vs distributed memory

Level 3 — Analysis

Exercise 3.1 (L3)

cores share one variable in state Shared (S) of the MSI protocol. Core 1 writes to it. Using the parent's rule "a write from S must invalidate the other caches," how many invalidate messages fire? If and each invalidate costs 30 ns of bus time (serialized), how long is the coherence burst?

Recall Solution 3.1

Invalidates needed . For : messages. Serialized on one bus: Why and not : the writer already owns its own copy — it only needs to silence the other caches. This growth is exactly why shared-memory bus machines top out around 8–64 cores. See Cache Coherence Protocols.

Exercise 3.2 (L3)

A struct packs int count[8] into one 64-byte cache line (each int = 8 bytes here → 8×8 = 64). Eight threads each write a different index. Explain why performance collapses, and give the one-line fix.

Recall Solution 3.2

This is False Sharing. All 8 counters live in one cache line. Coherence tracks whole lines, not individual bytes. So when Thread 0 writes count[0], the hardware invalidates that line in every other thread's cache — even though nobody else touched count[0]. Each write ping-pongs the line across all cores.

What it looks like: the line bounces core→core→core on every update, turning 8 independent writes into a fully serialized coherence storm (parent reports 5–10× slowdown).

Fix: pad each counter to its own cache line:

struct { int val; char pad[56]; } count[8]; // each entry = 64 bytes

Now writes hit different lines → no cross-invalidation.

Figure — Shared memory vs distributed memory

Level 4 — Synthesis

Exercise 4.1 (L4)

Multiply where are split row-wise across 4 nodes. Node 0 broadcasts all of ( compressed here for the arithmetic) to the other 3 nodes, one message each. Network: , . Compute the broadcast time. Then decide: is the communication or the computation likely the bottleneck if local compute is ?

Recall Solution 4.1

One message of bytes: Three sequential sends (Node 0 → Nodes 1,2,3): Compare to compute . Communication is only of runtime. Conclusion: computation dominates — this problem is "embarrassingly parallel" after the one-time broadcast. Pay 's transfer once, then crunch. This is distributed memory at its best.

Exercise 4.2 (L4)

100 GPUs do gradient AllReduce every batch. Model: , with , , bits, bits/s. Estimate . Which term dominates?

Recall Solution 4.2

Latency term: . Since : Bandwidth term: Bandwidth dominates utterly (80 ms vs 0.027 ms). For huge messages, — the exact opposite of Exercise 2.2 where tiny messages made latency king. The lesson: message size decides which term wins.


Level 5 — Mastery

Exercise 5.1 (L5)

A single node has 32 cores sharing memory (shared-memory model, OpenMP inside). You have 64 such nodes on an InfiniBand network (distributed model, Message Passing Interface (MPI) across). Total = 2048 cores. Design the programming model, and justify why a flat "MPI-everywhere" or "threads-everywhere" scheme is worse.

Recall Solution 5.1

Design: hybrid MPI + OpenMP.

  • Inside a node (32 cores): shared memory. Use OpenMP threads sharing one address space — communication is free implicit loads/stores, no message overhead.
  • Across nodes (64 nodes): distributed memory. Use MPI: one MPI rank per node, ranks exchange messages.

Why not threads-everywhere? You cannot — 64 separate machines have no shared address space; a thread on node 0 physically cannot load node 5's RAM. Shared memory does not span the network.

Why not MPI-everywhere (2048 ranks)? It works, but wastes the shared memory inside each node: cores on the same node would copy data through message buffers instead of just reading it directly. You'd pay 32×64 = 2048 ranks of coherence-free-but-copy-heavy traffic and needless memory duplication. Hybrid uses the right tool per level: implicit sharing where memory is shared, explicit messaging only where it must cross the network. See NUMA Architecture and Memory Consistency Models.

Exercise 5.2 (L5)

Steel-man the parent's claim "cache coherence is hidden message passing." Then state the one property distributed memory gives up in exchange for scalability, and one it keeps.

Recall Solution 5.2

Steel-man: In shared memory, writing a variable secretly triggers invalidate broadcasts to caches (Ex 3.1). Those invalidations are messages on the bus — you just never wrote send(). So shared memory hasn't abolished communication; it has hidden and automated it. When the hidden traffic saturates the bus, shared memory stops scaling — the same wall distributed memory faces, only invisible.

Distributed memory gives up: the convenience of implicit communication + automatic consistency — the programmer must now orchestrate every transfer and risk deadlock. Distributed memory keeps (and this is the win): independent, non-shared memory — no single bus every core fights over. That is why it scales to thousands of nodes while shared memory stalls at tens of cores.


Recall Self-test: fill the gap

Latency dominates message time when the message is ::: small (so is tiny next to ). A write from MSI state S must send this many invalidates among cores ::: . The unit of false sharing is the ::: cache line (~64 bytes), not the variable. The interface that decides shared vs distributed ::: load/store (implicit) vs send/receive (explicit).