Only if the actual addresses end up in distinct coherence lines under the selected layout. In the course's 64-byte-line model, fields at offsets 0 and 8 collide; offsets 0 and 64 do not. A struct's base alignment, field offsets and array stride all matter. Padding between fields is different from aligning each repeated object.
Worked layout, with byte offsets from the array base and line = offset // 64. A long is 8 bytes. The padded type is struct slot { long v; char pad[56]; }: 8 data bytes plus 56 pad bytes, so the object stride is 64.
Layout | Field byte ranges | Lines, offset // 64 | Consequence |
|---|---|---|---|
| c[0]: 0-7, c[1]: 8-15 | 0, 0 | False sharing: one permission unit for both writers. |
| s[0].v: 0-7, s[1].v: 64-71 | 0, 1 | Separate lines: each core keeps its own write permission. |
Same | s[0].v: 60-67, s[1].v: 124-131 | 0-1, 1-2 | Both fields span line 1, so the writers still false-share it. |
Unpadded 8-byte objects, stride 8, fields at 64 and 72 | 64-71, 72-79 | 1, 1 | Different objects can still false-share. |
The third row is why padding alone is not enough: the padding fixes the stride, but only alignment of the base to 64 puts each field at the start of its own line. (A compiler keeps a long 8-byte aligned, so offset 60 arises from a packed or hand-built buffer, but the same arithmetic applies to any base that is not a multiple of 64.) The allocation must therefore provide 64-byte alignment: alignas(64) on the type plus an allocator that honours it, such as C11 aligned_alloc(64, size) or C++17 aligned new. Default allocations are not 64-byte aligned (common 64-bit allocators guarantee 16 bytes), so a padded struct from a plain malloc can land exactly like row three.
A general guideline beyond the course model: some cores prefetch lines in adjacent pairs, so a write to one line can pull its 128-byte neighbour too. For the hottest independently written fields, separating by 128 bytes avoids that second-order effect. Check the target's documented coherence granule and prefetch behaviour before choosing 64 or 128.
For X/Y, separating lines preserves the logical result because each core writes a different word. For a genuinely shared X, copying the counter into per-core counters changes the algorithm: a reduction step must define how and when the global result is assembled. That can be a good design, but it is not padding alone.
The cost side includes more cache lines, potentially more page translations and poorer spatial locality for readers that need all fields. Group fields by the cores and phases that write them, not simply by source-code aesthetics. A producer-written payload and consumer-written status can need separate lines, while a read-mostly record may benefit from packing.
Use a controlled comparison with identical work and a stable writer placement. Inspect the final values as well as traffic. If the padded footprint triggers extra evictions, the saved holder transfers may be offset by capacity misses; measure both rather than declaring either layout universally better.
The total object stride and base alignment determine where adjacent writable fields land. Unused bytes do not prove that the actual field ranges occupy disjoint lines.