Skip to content
DenseChip
Learn
PracticeCode labInterview

DenseChip

Navigate

Menu

LearnPracticeCode labInterview

Tools

All toolsSV editorCode repoResume
DenseChip

Practice, courses, and chip-design tools in one focused workspace.

LearnPracticeCode labInterviewToolsLearning paths

© 2026 DenseChip

AboutContactPrivacyTerms
Skip to lesson

Course contents

  • How to Use This Course
  • Reading Path
  • Cache Coherency Glossary
  • The Shared Course Model
  • Cache Coherency Revision Sheet
  • Why Coherence Exists8
  • States and Protocols12
  • Transactions12
  • Races and Controller Design8
  • Progress and the Fabric16
  • Ordering and Synchronization12
  • Performance and Scalability12
    • False Sharing
      • True Versus False Sharing
      • Counting Coherence Traffic
      • Padding and Data Layout
      • Finding False Sharing
    • Directory Capacity and Sharers
    • Hotspots and Placement
  • Real Protocols12
  • SoC Integration12
  • Power, Clocking and Reliability12
  • Verification12
  • Debug and Bring-up12
  • Interview and Design Review17
  • Capstone: A Coherent Model Lab5
Loading lesson

‹False Sharing
  1. Topics
  2. Cache Coherency
  3. Performance and Scalability
  4. False Sharing
  5. Counting Coherence Traffic

Part 7 · Performance and Scalability · All levels

Counting Coherence Traffic

Count write-only MESI acquisitions and transfers for true sharing, false sharing and separated fields.

How much traffic does moving the field remove?

Use the same completed-operation schedule: C0 writes, C1 writes, repeat. X starts at 0 uncached. For false sharing add Y=0 at offset 8 in X's line; for separated fields move Y to the next line at offset 64. True sharing instead sends both writes to X. The Python uses byte addresses and eight words per line, preserving the entire current payload on each transfer. It counts messages per line, so a control can show that moving an unrelated field (offset 64, written by C1) does nothing for a truly shared X, and a failing case zeroes the payload on transfer to prove the value check bites.

Count the course's MESI write-only paths: a first GetM from U and its Data grant are two interconnect messages. A change of exclusive holder uses the H-buffered four-hop route, the course default: GetM, Fwd-GetM, Data to H, Data to the requester, four messages. The direct route is three messages (GetM, Fwd-GetM, Data straight to the requester) plus a completion to H: one fewer leg on C1's critical path, but still four messages per transfer in this count. Either way the comparison between layouts is unchanged, because it depends on the number of holder changes. Repeated writes by the current holder are local. Memory reads and local stores are not interconnect messages. There are no reads, evictions or overlapping transactions in this workload. Each operation completes before the next starts, including the course's assumed grant-delivery knowledge.

example.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# Simplified course model: MESI writes only, each GetM completes atomically,
# H's record is the holders dict, updated at once, no transients or races.
from collections import Counter
LINE, WORDS = 64, 8
def run(trace, broken=False):
    holders, payloads, expected, per_line = {}, {}, {}, Counter()
    for value, (core, address) in enumerate(trace, 1):
        line, word = address // LINE, (address % LINE) // 8
        if line not in holders:
            payloads[line] = [0] * WORDS
            per_line[line] += 2  # GetM, Data
        elif holders[line] != core:
            # Transfer the complete current payload, not stale memory bytes.
            payloads[line] = [0] * WORDS if broken else list(payloads[line])
            per_line[line] += 4  # GetM, Fwd-GetM, Data to H, Data to C
        holders[line] = core
        payloads[line][word] = value
        expected[address] = value
    actual = {a: payloads[a // LINE][(a % LINE) // 8] for a in expected}
    assert actual == expected
    return sum(per_line.values()), actual, per_line
def alternating(rounds, second_address):
    return [(core, 0 if core == 0 else second_address)
            for _ in range(rounds) for core in (0, 1)]
for rounds in range(1, 9):
    true, _, _ = run(alternating(rounds, 0))
    false, fields, _ = run(alternating(rounds, 8))
    padded, split, _ = run(alternating(rounds, 64))
    assert true == false == 2 + 4 * (2 * rounds - 1)
    assert padded == 4 and padded < false
    assert fields[0] == split[0] and fields[8] == split[64]
    # Control: X stays truly shared while C1 also writes an unrelated
    # field at offset 64. Padding elsewhere leaves X's traffic unchanged.
    control = [s for _ in range(rounds) for s in ((0, 0), (1, 0), (1, 64))]
    _, _, lines = run(control)
    assert lines[0] == true and lines[1] == 2
assert run(alternating(8, 0))[0] == run(alternating(8, 8))[0] == 62
assert run(alternating(8, 64))[0] == 4
_, _, lines = run([s for _ in range(8) for s in ((0, 0), (1, 0), (1, 64))])
assert lines[0] == 62
batch = [(0, 0)] * 8 + [(1, 8)] * 8
assert run(batch)[0] == 6
try:
    run(alternating(2, 8), broken=True)
except AssertionError:
    pass
else:
    raise AssertionError("lost neighbouring data was not detected")
print("PASS: separated fields reduce transfers, shared X still contends")

Illustrative 8 rounds, 16 stores

Messages

What changed

True sharing, same X

62

One cold acquisition, 15 holder changes.

False sharing, X/Y in one line

62

Same holder schedule despite independent fields.

Separated X/Y lines

4

Two cold acquisitions, then local stores.

Batched X/Y writes in one line

6

One cold acquisition and one holder change.

A writer schedule is mapped to line addresses and either produces a local hit or a counted permission acquisition.
The count follows line identity and holder changes rather than assigning a padding discount.

The test asserts a family of schedule lengths, exact field values, the per-line control and the table's 62, 4 and 6 counts. The 62-versus-4 result is a message count for the stated workload, not a measured speedup. A workload with capacity evictions, readers or shared logical state needs a different accounting experiment.

Common pitfall

Comparing padded private fields with a different number of useful updates, or counting one Data message as one byte regardless of payload size.

Interview

Explain why true and false sharing have equal traffic for the same line/holder schedule, but only false sharing can be removed by relocating an independent field.

Check yourself What would fail if a transfer rebuilt the line from zeroed memory?

The expected-field assertions fail when the second writer overwrites its word and loses the first writer's word. Lower traffic would not excuse that value loss.

Related topics

  • Shared course model

  • Why the chosen route has four legs

Previous

75 / 167

Next: Padding and Data Layout

Up next

Up nextPadding and Data Layout

Sign in to save progress

Practice this lessonReport an error

See also

  • Finding False Sharing