Skip to content

v-code01/unalign

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

4 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

unalign: on M4 unaligned access is free, but an unaligned atomic is a crash

ARM chips used to require naturally aligned memory access, and a lot of code still pads and aligns out of that habit. On an Apple M4 the habit is half right. Ordinary unaligned loads and stores are free, including ones that straddle a cache line or a page. But an atomic operation that crosses a 16-byte boundary does not run slowly, it faults: SIGBUS, process dead. This measures both.

Scalar access is free (8-byte load throughput, ns/load, BCa 95% CI, 9 runs)

offset throughput
aligned 0.236 [0.233, 0.236]
unaligned (off 1) 0.235 [0.235, 0.235]
unaligned (off 3) 0.235 [0.234, 0.236]
unaligned (off 7) 0.235 [0.232, 0.235]
cache-line crossing (off 60) 0.229 [0.227, 0.229]
cache-line crossing (off 61) 0.230 [0.228, 0.230]
page crossing (off 4090) 0.230 [0.221, 0.230]

Every offset lands at the same rate. There is no unaligned penalty on M4 for ordinary loads, not even for accesses that split a 64-byte cache line or a 4-kilobyte page. The ARM alignment folklore does not apply to regular memory here.

Atomics fault when they cross a 16-byte boundary (deterministic ok vs SIGBUS)

8-byte atomic:  aligned = ok    crosses 16-byte boundary = SIGBUS
                crosses cache line = SIGBUS    crosses page = SIGBUS
8-byte atomic within one 16-byte block: off 0 = ok, off 4 = ok, off 8 = ok,
                off 12 (spills past the 16-byte boundary) = SIGBUS
16-byte atomic: 16-byte aligned = ok    off 4 = SIGBUS    off 8 = SIGBUS

The rule is exact: an atomic access must lie entirely inside one 16-byte-aligned block. An 8-byte atomic can sit at any offset that keeps its eight bytes within one 16-byte block, but the moment it straddles a 16-byte boundary (and therefore any cache line or page) it raises SIGBUS. A 16-byte atomic must be fully 16-byte aligned. There is no slow path, only a fault.

The takeaway

You can read and write unaligned data on M4 as fast as aligned data, so packed and misaligned layouts are fine for ordinary access. But the instant you make one of those fields atomic, alignment stops being a performance question and becomes a crash. Casting a byte pointer inside a packed buffer to an atomic type, doing a lock-free operation on a field that is not 16-byte-block-aligned, an atomic that happens to span a cache line: all of these SIGBUS on M4, deterministically. Align anything you touch atomically to its size, and keep 16-byte atomics 16-byte aligned.

Reproduce

./run.sh

Needs clang for arm64 and Python 3 (standard library only). CPU only. The throughput varies run to run but stays flat across offsets; the fault map is deterministic.

Files

  • bench/unalign.c: the scalar throughput loop and the forked-child atomic fault map.
  • bench/sweep.py: the throughput sweep with BCa CIs and the fault map.
  • PREREG.md: the pre-registration.
  • run.sh: reproduces everything.

Scope

Apple M4, CPU only. The scalar claim is flat throughput (no unaligned penalty); the atomic claim is the exact 16-byte-block fault boundary.

License

MIT.

About

On Apple M4 unaligned scalar loads and stores run at full speed, even across cache lines and pages, but an atomic that crosses a 16-byte-aligned boundary raises SIGBUS: unaligned is free for memory and a crash for atomics.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors