Memory Hammer
Overview
memory_hammer repeatedly reads pairs of aggressor addresses that bracket an
untouched victim, then verifies the entire buffer. It looks for cells that
change value because of activity on neighbouring rows rather than because
anything wrote to them. Nothing is written during the hammer phase, so any
difference found at verification is a disturbance.
Execution Mechanics
Two aggressors sit 2 × --hammer_stride elements apart with the victim between
them. Alternating between the two is what forces a row to close and reopen;
hammering a single address would sit on an open row buffer and disturb nothing.
--hammer_stride defaults to 32768 elements (512 KiB). The stride that places
two aggressors in the same bank on different rows depends on the DRAM address
mapping, which is not public for most parts — so the stride is a knob to
sweep, not a value that is correct out of the box.
The cache problem — read this before trusting a result
A hammer only disturbs a victim if its reads actually reach DRAM. GPU caches will answer a tight loop over two addresses forever, and a cached read disturbs nothing. Choosing a cache-bypassing load instruction is not sufficient: if the aggressor working set fits in L2, the reads are still served on-chip.
Every thread therefore walks several aggressor pairs spread across the whole
allocation (--hammer_pairs, default 8), so the aggregate footprint exceeds
L2. Startup prints the comparison:
If that line reads FITS IN L2 - reads will be cache hits, the run is not
hammering anything. Raise --hammer_pairs. On a 12 GB Ampere card one pair
per thread sustains ~5.7e10 reads/s (cache speed) while eight pairs drop to
~2.2e10 reads/s — the card's DRAM bandwidth.
Target Subsystems
- Primary Target: DRAM row-to-row disturbance between physically adjacent cells.
- Secondary: Memory controller behaviour under repeated single-row access.
Failure Symptoms
Critical Failures
- Disturbed cell: Reported with the victim index and an
XORmask of the flipped bits. - A victim that flips only at one particular stride is the interesting case. It suggests a genuine neighbour relationship rather than a weak cell, which would fail regardless of stride.
- Confirm any hit by re-running at the same stride and by running
memory_retentionover the same region. A cell failing both is weak, not disturbed.
Honest limits
Without the physical address mapping, Pantheon cannot prove a read opened a
given row or that two addresses share a bank. The metric is named
aggressor-reads/s — what is actually measured — rather than
"activations/s", which would imply a DRAM-level guarantee this test cannot
make. A PASS means no disturbance was observed at this stride, not that
the part is immune.
Usage
Sweeping the stride, recording every hit:
for s in 8192 16384 32768 65536; do
./build/memory_hammer 0 60 50 --verify --hammer_stride $s \
--fault_map hammer_$s.csv
done
Result
Throughput reports aggressor-reads/s, and startup reports the aggressor
footprint against L2. Read the two together — a high read rate with a footprint
that fits in L2 means the cache absorbed the run.