The full picture
// 1. Iteration order matches layout (row-major): ~5-10x on large matrices
for (int r = 0; r < N; r++)
for (int c = 0; c < N; c++) sum += m[r][c]; // sequential lines ✅
// column-first walks N lines per element — every access a fresh line ❌
// 2. Structure of arrays for the hot loop (no object per point):
double[] xs, ys, zs; // 3 sequential streams, prefetcher happy
// vs List<Point3D> — header + pointer + scattered heap nodes per element
// 3. Batch to fit cache (temporal locality):
for (int base = 0; base < n; base += 4096) // tile fits in L2
for (Pass p : passes) p.apply(data, base, 4096);
// vs each pass streaming the WHOLE array: n/4096x more DRAM traffic- JVM specifics: object headers (12–16 bytes) + reference indirection tax every node; primitive arrays and libraries like fastutil/Eclipse Collections exist for exactly this; Valhalla value objects aim to give 'flat' data classes.
- GC interaction, honestly: copying collectors can improve locality (compaction, allocation-order copying), but you don't control layout precisely — the reliable strategy is arrays for the proven-hot 5%, plain objects for the readable 95%.
- When NOT to bother: I/O-bound services, cold paths, anything the profiler hasn't indicted — cache tuning is a targeted optimization with a readability bill; the senior posture is 'design data layout consciously in the hot loop, and only there'.
- Verification loop: JMH benchmark +
perf stat(L1/LLC miss rates, IPC) or JFR allocation/CPU profiles — claims about cache behavior without counters are astrology.