功能对齐在现代处理器上究竟有多重要?

时间:2014-03-06 20:28:28

标签: performance assembly alignment x86-64

当我在amd64或x86系统上使用最新编译器编译C代码时,函数将对齐到16个字节的倍数。这种对齐在现代处理器上实际上有多重要?调用未对齐函数会产生巨大的性能损失吗?

基准

我运行了以下微基准测试(call.S):

// benchmarking performance penalty of function alignment.
#include <sys/syscall.h>

#ifndef SKIP
# error "SKIP undefined"
#endif

#define COUNT 1073741824

        .globl _start
        .type _start,@function
_start: mov $COUNT,%rcx
0:      call test
        dec %rcx
        jnz 0b
        mov $SYS_exit,%rax
        xor %edi,%edi
        syscall
        .size _start,.-_start

        .align 16
        .space SKIP
test:   nop
        rep
        ret
        .size test,.-test

使用以下shell脚本:

#!/bin/sh

for i in `seq 0 15` ; do
        echo SKIP=$i
        cc -c -DSKIP=$i call.S
        ld -o call call.o
        time -p ./call
done

在根据/proc/cpuinfo将自身标识为 Intel(R)Core(TM)i7-2760QM CPU @ 2.40GHz 的CPU上。这个偏差对我来说没什么影响,基准测试经常持续1.9秒。

另一方面,在另一个CPU报告自身为 Intel(R)Core(TM)i7 CPU L 640 @ 2.13GHz 的系统上,基准测试需要6.3秒,除非是你有一个14或15的偏移量,代码需要7.2秒。我认为这是因为该函数开始跨越多个缓存行。

1 个答案:

答案 0 :(得分:3)

TL; DR :缓存对齐很重要。你不想要你不会执行的字节。

至少,您希望避免在执行第一个指令之前获取指令。由于这是一个微基准测试,你很可能没有看到任何差异,但想象一下完整的程序,如果你在一堆函数上有额外的缓存缺失,因为第一个字节没有对齐到缓存行,你最终必须为函数的最后N个字节获取一个新的缓存行(其中N <=你缓存但没有使用的函数之前的字节数)。

Intel's optimization manual说:

  

3.4.1.5代码对齐

     

仔细安排代码可以增强缓存和内存位置。基本块的可能序列应该在存储器中连续布局。这可能涉及从序列中删除不太可能的代码,例如处理错误条件的代码。看到   关于优化指令预取器的第3.7节“预取”。

     

3-8汇编/编译器编码规则12.(M影响,H一般性)所有分支目标应为16字节对齐。

     

汇编/编译器编码规则13.(M影响,H一般性)如果条件的主体不可能被执行,则应将其放在程序的另一部分中。如果它不太可能被执行并且代码局部性是个问题,那么它应该放在不同的代码页上。

它还有助于解释为什么您没有注意到您的计划有任何差异。所有代码都缓存一次,永远不会离开缓存(当然是模数上下文切换)。