Reading the same eight columns from every matrix row does not guarantee the same cache performance when row width changes. The width also changes the addresses of those columns. A cache placement policy determines where fetched memory blocks can go in a CPU cache, so different addresses can produce different cache behavior. section 12.1.5
func calculateSum512(s [][512]int64) int64 { #1
var sum int64
for i := 0; i < len(s); i++ {
for j := 0; j < 8; j++ {
sum += s[i][j]
}
}
return sum
}
func calculateSum513(s [][513]int64) int64 { #2
// Same implementation as calculateSum512
}
Compare the two Go functions: each sums only the first eight columns, but one matrix has 512 columns per row and the other has 513. section 12.1.5
A set-associative cache partitions its lines into sets. In a simplified example with a 512-byte L1D cache and eight 64-byte lines, blocks from successive matrix rows can map to the same set. The illustrated third block replaces a line already in set 0. Placement can therefore matter even when the loop never reads the extra columns. section 12.1.5
Benchmark conditions matter: with a new matrix for each run, the two functions show no observed difference. When the same matrix is reused, the 513-column version runs about 50% faster on the reported machine. Treat that result as a reason to investigate address mapping and cache reuse, not as a rule to add a column to every matrix. section 12.1.5
Placement is distinct from access predictability. A slice walked at a unit stride has favorable spatial locality; following linked-list pointers is harder for the CPU to predict, even if the nodes happen to be contiguous. Check both the access pattern and cache placement when diagnosing a surprising memory-access benchmark. section 12.1.4 section 12.1.5
