Compilers and languages still require full natural alignment. A 4-byte integer is placed on a 4-byte boundary, a 16-byte vector on a 16-byte boundary, a 32-byte vector on a 32-byte boundary, and so on. That rule is older than the cores it still targets.
On a modern x64 processor, an access that misses natural alignment and still stays inside one cache line is often as fast as the fully aligned access. The costs that remain show up when an access crosses a cache line, and they are larger again when an access crosses a page. This project measures where alignment still matters, and which kind of alignment it is.
alignbench.asm is a single-file x64 microbenchmark. The timed loops are hand-written machine code. There is no compiler and no optimizer on that path, so each row runs the instruction the source names.
Each group runs the same kernel at two or more addresses. One row is the baseline. The others move the address. The program warms the caches, then times several passes with the processor timestamp counter. The report gives the mean, minimum, maximum, and spread of those passes, plus the ratio of each row to the baseline of its group. A ratio near one means that address change did not change the cost.
Results are printed on the console and written to out/alignbench_results.txt. Build with build.bat from this directory. config.bat holds the path to the Visual Studio x64 tools; set VCVARS64 in the environment to override it for one run. build.bat run builds and runs. The executable is out\alignbench.exe.
The same kernels are repeated over three working sets: a buffer that stays in L1, a buffer that misses L1 and is served from L2, and a streaming buffer larger than the last-level cache, so that traffic comes from DRAM. Widths run from bytes and words through 16-byte, 32-byte, and, when the processor and the OS enable it, 64-byte accesses. Loads, stores, and copies are all included. A few groups also time locked updates, store-to-load forwarding, and packed flag layouts, where the width of each element changes how much memory moves per value.
The rows are different kinds of alignment, not one switch called "unaligned."
- Full alignment. The address is a multiple of the access width. This is the alignment compilers already emit. Some instructions require it and fault on any other address. Those instructions are timed only at a legal address, next to the unaligned instruction form on that same address.
- Off alignment, inside one cache line. The address is not a multiple of the width, and the first and last byte of the access still sit in the same 64-byte cache line. A dense array that starts one byte off produces this case for most of its elements.
- Cache-line split. The access starts in one 64-byte line and ends in the next. In a long array that is only slightly off, only some elements split, so the penalty is diluted. Other rows place every access across the line boundary, so the split is the whole measurement. Wider accesses split more often: a 64-byte access has no address that is off alignment and still inside one line.
- Page split. The access crosses a 4 KiB page boundary. That boundary is also a TLB boundary. The aligned row and the split row walk the same pages; the ratio is the cost of straddling the page.
The absolute rates in a report follow the core clock, the memory clock, and whatever else the machine is doing during the run. Operating-system power and performance settings can raise those clocks and can change how often the core is interrupted. Firmware menus in the BIOS or UEFI often expose related options for processor performance and for memory. Either place may offer settings that improve the numbers this benchmark prints. Read the ratios inside one run: those rows shared the same settings.
The code is covered by LICENSE. Use is unrestricted. There is no warranty, no guarantee, and no liability.