ECE 5755: Modern Computer Systems and Architecture
Lecture 14: Multi-core/Networking
Susan Eggers, widely recognized as one of the leading computer architects, is the first woman to receive the Eckert-Mauchly Award. Eggers’ early work focused on maintaining accurate and efficient cache coherency in shared memory processors. Eggers is best known for her foundational work in developing and helping to commercialize simultaneous multithreaded (SMT) processors. Today, SMT as developed by Eggers and her colleagues remains an essential component in commercial manufacturers, including Intel and IBM.
�
Logistics
Photos, illustrations, graphics here.
Today’s agenda
Photos, illustrations, graphics here.
Parallelism: Superscalar processors
Fetch/
Decode
ALU
Execution unit
Execution Context
Register 0
Register 1
Register 2
Register 3
Register 4
Register 5
L1 Instruction Cache
L1 Data
Cache
L2 cache
Mem controller
Fetch/
Decode
ALU
Execution unit
Out-of-order control logic
Superscalar execution: processor automatically finds independent instructions in an instruction sequence and can execute them in parallel on multiple execution units
Photos, illustrations, graphics here.
Typical dual-issue pipeline
Photos, illustrations, graphics here.
Typical dual-issue pipeline
Photos, illustrations, graphics here.
Computation Taxonomy
Photos, illustrations, graphics here.
Multi-core era processors
Execution Context
Register 0
Register 1
Register 2
Register 3
Register 4
Register 5
Fetch/
Decode
ALU
Execution unit
Rather than use transistors to increase sophistication of processor logic that accelerates a single instruction stream
(e.g., out-of-order, speculation)
Use increasing transistor count to add more cores to the processor
Photos, illustrations, graphics here.
Multi-core era processors
Execution Context
Register 0
Register 1
Register 2
Register 3
Register 4
Register 5
Fetch/
Decode
ALU
Execution unit
Execution Context
Register 0
Register 1
Register 2
Register 3
Register 4
Register 5
Fetch/
Decode
ALU
Execution unit
Simpler cores: each core may be slower at running a single instruction stream than a more sophisticated core (e.g., 25% slower)
But there are now two cores: 2 * 0.75 = 1.5x (potential for speedup!)
Photos, illustrations, graphics here.
Four cores: compute four elements in parallel
Photos, illustrations, graphics here.
Sixteen cores: compute sixteen elements in parallel
Photos, illustrations, graphics here.
Another idea: Add more execution units to increase compute capacity
Execution Context
Register 0
Register 1
Register 2
Register 3
Register 4
Register 5
Fetch/
Decode
ALU 0
Another idea…
Amortize cost/complexity of managing an instruction stream across many ALU’s
SIMD processing
Single instruction, multiple data
Same instruction broadcast to all ALUs
This operation is executed in parallel on all ALUs
ALU 0
ALU 0
ALU 0
ALU 0
ALU 0
ALU 0
ALU 0
Photos, illustrations, graphics here.
16 SIMD cores: 128 elements in parallel
Photos, illustrations, graphics here.
Common terminology
Photos, illustrations, graphics here.
SIMD execution: modern CPU examples
Photos, illustrations, graphics here.
Lab 2: SIMD with AVX intrinsics
256-bit register = 8 floats
x0
x1
x2
x3
x4
x5
x6
x7
+
y0
y1
y2
y3
y4
y5
y6
y7
x0+y0
x1+y1
x2+y2
x3+y3
x4+y4
x5+y5
x6+y6
x7+y7
One instruction, 8 additions
(e.g., _mm256_add_ps)
Lab 2 combines both ideas: tiling keeps data in cache; AVX does 8 operations per instruction
Sources: Intel Intrinsics Guide; GCC manual, “x86 Options” (-mavx2, -mfma).
Photos, illustrations, graphics here.
SIMD execution: modern GPU examples
Photos, illustrations, graphics here.
Photos, illustrations, graphics here.
Photos, illustrations, graphics here.
SIMD vs. MIMD
Photos, illustrations, graphics here.
The key shift: cores must share data
X = 5
read X → ?
Core 0
Cache
Core 1
Cache
Interconnect
Memory
Which copy of X is current: Core 0’s cache, or memory?
Today: interconnects + shared memory Oct 19 & 21: cache coherence Oct 26 & 28: memory consistency
Photos, illustrations, graphics here.
Taxonomy of Parallel (MIMD) Processors
Photos, illustrations, graphics here.
UMA vs. NUMA: Why it matters
Photos, illustrations, graphics here.
UMA vs. NUMA: Why it matters
Photos, illustrations, graphics here.
Interconnection Networks
Photos, illustrations, graphics here.
Interconnect 1: Bus
Photos, illustrations, graphics here.
Interconnect 2: Crossbar Switch
Photos, illustrations, graphics here.
Interconnect 3: Multistage Network
Photos, illustrations, graphics here.
Interconnect 4: 2D Torus
Photos, illustrations, graphics here.
Interconnect 5: Hypercube
Photos, illustrations, graphics here.
Real systems: on-chip networks
cores + LLC slices
I/O die
I/O die
Ring (1D torus)
Intel Sandy Bridge (2011): ring links cores, LLC slices, graphics
Servers outgrew it: Xeon moved to a mesh (next)
2D mesh
Intel Xeon Skylake-SP (2017): mesh replaces the ring
Connects up to 28 cores on one die
Hub (chiplets)
AMD EPYC Rome (2nd gen): up to 8 core chiplets around one I/O die
Central I/O die cut memory latency vs. 1st gen
Same four questions as before: latency, bandwidth, cost (wires, switches), scalability
Sources: Lempel (Intel), Sandy Bridge, Hot Chips 23, 2011; AnandTech, Skylake-SP mesh, Jul 2017; Naffziger (AMD), IEEE Spectrum, Jun 2023.
Photos, illustrations, graphics here.
Real systems: AI accelerator networks
GPU
GPU
GPU
GPU
GPU
GPU
GPU
GPU
GPU
GPU
GPU
GPU
GPU
GPU
GPU
GPU
Switch
Switch
Switch
Switch
Switched: NVIDIA GB200 NVL72
72 GPUs, 9 NVLink switch trays
Every GPU talks to every other GPU (like a crossbar)
1.8 TB/s per GPU; 130 TB/s all-to-all total
Torus: Google TPU v4
4,096 chips; 6 inter-chip links each
Rack = 4×4×4 mesh; optical switches join 64 racks into a (twisted) 3D torus
Edge links wrap around to the far side (2D slice shown)
Think-pair-share: why might Google pick a torus while NVIDIA picks switches?
Sources: NVIDIA Technical Blog, GB200 NVL72 designs to OCP, Oct 2024; Jouppi et al., “TPU v4,” ISCA 2023.
Photos, illustrations, graphics here.
Real systems: the datacenter network
Spine
Spine
Spine
Spine
Leaf
Leaf
Leaf
Leaf
Leaf
Leaf
servers (each leaf = a top-of-rack switch)
Every scale, same questions: latency, bandwidth, cost, scalability
Sources: Al-Fares et al., “A Scalable, Commodity Data Center Network Architecture,” SIGCOMM 2008; Poutievski et al., “Jupiter Evolving,” SIGCOMM 2022.
Photos, illustrations, graphics here.
Shared memory vs. Message Passing
Photos, illustrations, graphics here.
Shared memory vs. Message Passing
Photos, illustrations, graphics here.
Taxonomy of multi-processors
Photos, illustrations, graphics here.
Shared Memory Systems Design Challenges
Photos, illustrations, graphics here.
Shared Memory Systems Design Challenges
Photos, illustrations, graphics here.
The memory coherence problem
Photos, illustrations, graphics here.
5 minute break!
Photos, illustrations, graphics here.
Shared-Memory Multiprocessors
P0
P1
P2
P3
Memory
Photos, illustrations, graphics here.
Shared-Memory Multiprocessors
P0
P1
P2
P3
$
M0
Router/interface
Interconnect
$
M1
Router/interface
$
M2
Router/interface
$
M3
Router/interface
Photos, illustrations, graphics here.
Problems with the Intuition
Problems with the Intuition
Problems with the Intuition
Coherence
Wr X
Wr X
“Reading X gets the value of the last write to X”
Rd X
P1
P2
P3
Coherence
2) “Reading X gets the value of the last write to X”
Wr X
Wr X
Rd X
I wrote X last
Blue wrote X last
P1
P2
P3
Without Coherence
Wr X
Wr X
Rd X
Which X?!
Cache X
Cache X
(The coherence invariants prevent this from happening)
How to decide who wrote last?
P1
P2
P3
Coherence enforces Order of Writes
Wr X
Wr X
Coherence defines a total order of writes to a single memory location.
Wr X
Wr X
OR
How do we provide this guarantee?
Need to serialize somewhere…
P1
P2
P1
P2
Shared Caches
Intel Core 2 Duo (Conroe)
Shared Cache Implementation
Interconnect
P0
P1
P2
Memory | A | 500 |
B | 0 |
Shared�Cache | Tag | Data | | |
| | | | |
| | | |
Shared Cache Implementation
Interconnect
P0
P1
P2
Memory | A | 500 |
B | 0 |
Shared�Cache | Tag | Data | | |
| | | | |
| | | |
Load [A]
1
2
Shared Cache Implementation
Interconnect
P0
P1
P2
Memory | A | 500 |
B | 0 |
Shared�Cache | Tag | Data | | |
A | 500 | | | |
| | | |
Load [A]
1
2
3
4
(500)
Shared Cache Implementation
Interconnect
P0
P1
P2
Memory | A | 500 |
B | 0 |
Shared�Cache | Tag | Data | | |
A | 500 | | | |
| | | |
Load [A]
1
2
3
4
(500)
Shared Cache Implementation
Shared�Cache | Tag | Data | | |
A | 400 | | | |
| | | |
Interconnect
P0
P1
P2
Memory | A | 500 |
B | 0 |
1
Store 400 -> [A]
Shared Cache Implementation
Shared�Cache | Tag | Data | State | |
A | 400 | Dirty | | |
| | | |
Interconnect
P0
P1
P2
Memory | A | 500 |
B | 0 |
1
Store 400 -> [A]
2
Adding Private Caches
Interconnect
P0
Cache | ||
Tag | Data | |
| | |
| | |
Cache | ||
Tag | Data | |
| | |
| | |
Cache | ||
Tag | Data | |
| | |
| | |
P1
P2
Memory | A | |
B | |
Shared�Cache | Tag | Data | State | |
| | | | |
| | | |
Adding Private Caches
Interconnect
P0
Cache | ||
Tag | Data | |
| | |
| | |
Cache | ||
Tag | Data | |
| | |
| | |
Cache | ||
Tag | Data | |
| | |
| | |
P1
P2
Memory | A | 500 |
B | |
Shared�Cache | Tag | Data | State | |
| | | | |
| | | |
Load [A]
1
2
3
Adding Private Caches
Interconnect
P0
Cache | ||
Tag | Data | |
| | |
| | |
Cache | ||
Tag | Data | |
A | 500 | |
| | |
Cache | ||
Tag | Data | |
| | |
| | |
P1
P2
Memory | A | 500 |
B | |
Shared�Cache | Tag | Data | State | |
A | 500 | Clean | | |
| | | |
Load [A]
1
2
3
4
(500)
5
6
Adding Private Caches
Interconnect
P0
Cache | ||
Tag | Data | |
| | |
| | |
Cache | ||
Tag | Data | |
A | 400 | |
| | |
Cache | ||
Tag | Data | |
| | |
| | |
P1
P2
Memory | A | 500 |
B | |
Shared�Cache | Tag | Data | State | |
A | 500 | Clean | | |
| | | |
Store 400 -> [A]
1
Adding Private Caches
Interconnect
P0
Cache | ||
Tag | Data | |
| | |
| | |
Cache | ||
Tag | Data | State |
A | 400 | Dirty |
| | |
Cache | ||
Tag | Data | |
| | |
| | |
P1
P2
Memory | A | 500 |
B | |
Shared�Cache | Tag | Data | State | |
A | 500 | Clean | | |
| | | |
Store 400 -> [A]
1
2
Adding Private Caches
Interconnect
P0
Cache | ||
Tag | Data | |
| | |
| | |
Cache | ||
Tag | Data | State |
A | 400 | Dirty |
| | |
Cache | ||
Tag | Data | |
| | |
| | |
P1
P2
Memory | A | 500 |
B | |
Shared�Cache | Tag | Data | State | |
A | 500 | Clean | | |
| | | |
2
Private Cache Problem: Incoherence
Interconnect
P0
Cache | ||
Tag | Data | |
| | |
| | |
Cache | ||
Tag | Data | State |
A | 400 | Dirty |
| | |
Cache | ||
Tag | Data | |
| | |
| | |
P1
P2
Memory | A | 500 |
B | |
Shared�Cache | Tag | Data | State | |
A | 500 | Clean | | |
| | | |
Load [A]
2
1
Private Cache Problem: Incoherence
Interconnect
P0
Cache | ||
Tag | Data | |
A | 500 | |
| | |
Cache | ||
Tag | Data | State |
A | 400 | Dirty |
| | |
Cache | ||
Tag | Data | |
| | |
| | |
P1
P2
Memory | A | 500 |
B | |
Shared�Cache | Tag | Data | State | |
A | 500 | Clean | | |
| | | |
Load [A]
2
1
(500)
3
4
Private Cache Problem: Incoherence
Interconnect
P0
Cache | ||
Tag | Data | |
A | 500 | |
| | |
Cache | ||
Tag | Data | State |
A | 400 | Dirty |
| | |
Cache | ||
Tag | Data | |
| | |
| | |
P1
P2
Memory | A | 500 |
B | |
Shared�Cache | Tag | Data | State | |
A | 500 | Clean | | |
| | | |
Load [A]
2
1
(500)
3
4
Uh, Oh
Bus-based Multiprocessor
P0
P1
P2
P3
$
M0
Bus
$
M1
$
M2
$
M3
Snoopy Hardware Cache Coherence
CPU
D$
data
D$ tags
CC
bus
VI (MI) Coherence Protocol
I
V
R⇒BR, W⇒BW
BR/BW⇒SD, WB⇒SD
R/W
BR/BW
Three processor-initiated events
R: read W: write WB: write-back
One response event: SD: send data
Two remote-initiated events
BR: bus-read, read miss from another processor
BW: bus-write, write miss from another processor
VI Protocol State Transition Table
| This Processor | Other Processor | ||
State | Load | Store | Load Miss | Store Miss |
Invalid (I) | Miss => V | Miss => V | --- | --- |
Valid (V) | Hit | Hit | Send Data => I | Send Data => I |
VI Protocol (Write-Back Cache)
Processor 0
0: addi $r3,$r1,&accts
1: lw $r4,0($r3)
2: blt $r4,$r2,6
3: sub $r4,$r4,$r2
4: sw $r4,0($r3)
5: jal dispense_cash
Processor 1
0: addi $r3,$r1,&accts
1: lw $r4,0($r3)
2: blt $r4,$r2,6
3: sub $r4,$r4,$r2
4: sw $r4,0($r3)
5: jal dispense_cash
500
V:500
500
V:400
500
I:
400
V:400
400
V:300
CPU0
Mem
CPU1
Is this a good protocol? What is it actually doing? Can we do better? ☺
VI → MSI
I
M
W⇒BW
BW⇒SD, WB⇒SD
R⇒,W⇒
BR⇒,BW⇒
S
W⇒BW
R⇒,BR⇒
BR⇒SD
BW⇒
R⇒BR
MSI Protocol State Transition Table
| This Processor | Other Processor | ||
State | Load | Store | Load Miss | Store Miss |
Invalid (I) | Miss => S | Miss => M | --- | --- |
Shared (S) | Hit | Upg Miss => M | --- | => I |
Modified (M) | Hit | Hit | Send Data => S | Send Data => I |
MSI Protocol (Write-Back Cache)
Processor 0
0: addi $r3,$r1,&accts
1: lw $r4,0($r3)
2: blt $r4,$r2,6
3: sub $r4,$r4,$r2
4: sw $r4,0($r3)
5: jal dispense_cash
Processor 1
0: addi $r3,$r1,&accts
1: lw $r4,0($r3)
2: blt $r4,$r2,6
3: sub $r4,$r4,$r2
4: sw $r4,0($r3)
5: jal dispense_cash
500
S:500
500
M:400
500
S:400
400
S:400
I:
400
M:300
CPU0
Mem
CPU1
MSI Example: Step #1
P0
Cache | ||
Addr | Data | State |
-- | -- | -- |
-- | -- | -- |
Shared�Cache | Addr | Data | State |
A | 1000 | Modified | |
B | 0 | Idle |
Bus
Cache | ||
Addr | Data | State |
A | 500 | M |
-- | -- | -- |
Cache | ||
Addr | Data | State |
-- | -- | -- |
-- | -- | -- |
P1
P2
Load A
Miss!
Memory | A | 1000 |
B | 0 |
MSI Example: Step #2
P0
Cache | ||
Addr | Data | State |
-- | -- | -- |
-- | -- | -- |
Bus
Cache | ||
Addr | Data | State |
A | 500 | M |
-- | -- | -- |
Cache | ||
Addr | Data | State |
-- | -- | -- |
-- | -- | -- |
P1
P2
Load A
LdMiss: Addr=A
Memory | A | 1000 |
B | 0 |
Shared�Cache | Addr | Data | State |
A | 1000 | Modified | |
B | 0 | Idle |
MSI Example: Step #3
P0
Cache | ||
Addr | Data | State |
-- | -- | -- |
-- | -- | -- |
Bus
Cache | ||
Addr | Data | State |
A | 500 | S |
-- | -- | -- |
Cache | ||
Addr | Data | State |
-- | -- | -- |
-- | -- | -- |
P1
P2
Load A
Response: Addr=A, Data=500
Memory | A | 1000 |
B | 0 |
Shared�Cache | Addr | Data | State |
A | 1000 | Modified | |
B | 0 | Idle |
MSI Example: Step #4
P0
Cache | ||
Addr | Data | State |
A | 500 | S |
-- | -- | -- |
Bus
Cache | ||
Addr | Data | State |
A | 500 | S |
-- | -- | -- |
Cache | ||
Addr | Data | State |
-- | -- | -- |
-- | -- | -- |
P1
P2
Load A
Response: Addr=A, Data=500
Memory | A | 1000 |
B | 0 |
Shared�Cache | Addr | Data | State |
A | 500 | Shared, Dirty | |
B | 0 | Idle |
MSI Example: Step #5
P0
Cache | ||
Addr | Data | State |
A | 500 | S |
-- | -- | -- |
Bus
Cache | ||
Addr | Data | State |
A | 500 | S |
-- | -- | -- |
Cache | ||
Addr | Data | State |
-- | -- | -- |
-- | -- | -- |
P1
P2
Load A <- 500
Memory | A | 1000 |
B | 0 |
Shared�Cache | Addr | Data | State |
A | 500 | Shared, Dirty | |
B | 0 | Idle |
MSI Example: Step #6
P0
Cache | ||
Addr | Data | State |
A | 500 | S |
-- | -- | -- |
Bus
Cache | ||
Addr | Data | State |
A | 500 | S |
-- | -- | -- |
Cache | ||
Addr | Data | State |
-- | -- | -- |
-- | -- | -- |
P1
P2
Store 400 -> A
Miss!
Memory | A | 1000 |
B | 0 |
Shared�Cache | Addr | Data | State |
A | 500 | Shared, Dirty | |
B | 0 | Idle |
MSI Example: Step #7
P0
Cache | ||
Addr | Data | State |
A | 500 | S |
-- | -- | -- |
Bus
Cache | ||
Addr | Data | State |
A | 500 | S |
-- | -- | -- |
Cache | ||
Addr | Data | State |
-- | -- | -- |
-- | -- | -- |
P1
P2
Store 400 -> A
Miss!
UpgradeMiss: Addr=A
Memory | A | 1000 |
B | 0 |
Shared�Cache | Addr | Data | State |
A | 500 | Shared, Dirty | |
B | 0 | Idle |
MSI Example: Step #8
P0
Cache | ||
Addr | Data | State |
A | 500 | S |
-- | -- | -- |
Bus
Cache | ||
Addr | Data | State |
A | -- | I |
-- | -- | -- |
Cache | ||
Addr | Data | State |
-- | -- | -- |
-- | -- | -- |
P1
P2
Store 400 -> A
Miss!
UpgradeMiss: Addr=A
Memory | A | 1000 |
B | 0 |
Shared�Cache | Addr | Data | State |
A | 500 | Modified | |
B | 0 | Idle |
MSI Example: Step #9
P0
Cache | ||
Addr | Data | State |
A | 500 | M |
-- | -- | -- |
Bus
Cache | ||
Addr | Data | State |
A | -- | I |
-- | -- | -- |
Cache | ||
Addr | Data | State |
-- | -- | -- |
-- | -- | -- |
P1
P2
Store 400 -> A
Miss!
Memory | A | 1000 |
B | 0 |
Shared�Cache | Addr | Data | State |
A | 500 | Modified | |
B | 0 | Idle |
MSI Example: Step #10
P0
Cache | ||
Addr | Data | State |
A | 400 | M |
-- | -- | -- |
Bus
Cache | ||
Addr | Data | State |
A | -- | I |
-- | -- | -- |
Cache | ||
Addr | Data | State |
-- | -- | -- |
-- | -- | -- |
P1
P2
Store 400 -> A
Miss!
Memory | A | 1000 |
B | 0 |
Shared�Cache | Addr | Data | State |
A | 500 | Modified | |
B | 0 | Idle |
Cache Coherence and Cache Misses
Hmm.. What happens in this case? (MSI)
load A miss
store A
Processor 0
Hmm.. What happens in this case?
load A miss
store A miss again! (upgrade miss)!
Processor 0
MESI (4-state) Invalidation Protocol
MESI Example
Processor 0
0: addi $r3,$r1,&accts
1: lw $r4,0($r3)
2: blt $r4,$r2,6
3: sub $r4,$r4,$r2
4: sw $r4,0($r3)
Processor 1
0: addi $r3,$r1,&accts
1: lw $r4,0($r3)
2: blt $r4,$r2,6
3: sub $r4,$r4,$r2
4: sw $r4,0($r3)
500
E:500
500
M:400
500
S:400
400
S:400
I:
400
M:300
CPU0
Mem
CPU1
(No miss!)
MESI Protocol State Transition Table
| This Processor | Other Processor | ||
State | Load | Store | Load Miss | Store Miss |
Invalid (I) | Miss => S or E | Miss => M | --- | --- |
Shared (S) | Hit | Upg Miss => M | --- | => I |
Exclusive (E) | Hit | Hit => M | Send Data => S | Send Data => I |
Modified (M) | Hit | Hit | Send Data => S | Send Data => I |
Can we do better than MESI?
Can we do better than MESI?