2010-01-05 62 views
26

有人能给我一个简短而合理的解释,说明为什么编译器为了对齐其成员而向数据结构添加填充?我知道这样做是为了让CPU可以更高效地访问数据,但我不明白为什么会这样。为什么数据结构对齐对于性能很重要?

如果这只与CPU有关,为什么在Linux中8个字节对齐的双4字节和Windows对齐8个字节?

+2

有两个独立但相关的问题:数据比对和数据结构的填充 – 2010-01-05 13:21:03

+0

GCC对齐8个字节dobules以及在x86机器上,虽然,同微软的编译器。 – nos 2010-01-05 13:38:46

+0

如果CPU以4字节块读取数据,为什么双8字节对齐?那么双倍是8或4字节对齐应该没关系,不是吗? – Mat 2010-01-05 13:51:56

回答

15

对齐有助于CPU以有效的方式获取来自存储器的数据:以下高速缓存未命中/冲洗,更少的总线事务等

一些存储器类型(例如RDRAM,DRAM等)需要以结构化的要被访问方式(对齐“单词”和“突发事务”,即一次处理多个单词)以产生有效的结果。这是由于许多事情当中其中:

  1. 建立时间:时间花费在存储设备访问存储位置
  2. 总线仲裁开销即许多设备可能需要访问存储设备

“填充”用于纠正数据结构的对齐以优化传输效率。


换句话说,访问“错位”结构会降低整体性能。一个很好的例子就是:假设一个数据结构是错误对齐的,并且要求CPU /内存控制器执行2个总线事务(而不是1个)以获取所述结构,所以性能因此较低。

+0

那么究竟发生了什么,如果我们说一个浮点数是1byte对齐? – Mat 2010-01-05 13:30:32

+0

@Mat:然后,根据“哪里”的“浮点变量”最终被分配到内存中,访问这个“浮点变量”的效率会有所不同。 – jldupont 2010-01-05 13:36:46

+0

但我理解正确,访问严重对齐的浮动性能不会比访问正确对齐的双重性能更差吗? – Mat 2010-01-05 13:46:48

12

CPU以4字节为单位从内存中提取数据(实际上它取决于硬件的某些类型的硬件的8或其他值,但让我们坚持4以保持它简单), 一切都很好,如果数据从一个可以被4除的地址开始,CPU进入内存地址并加载数据。

现在假设在不可分割由4说为了简单在地址1的缘故一个地址的数据开始时,CPU必须把从地址0的数据,然后应用一些算法在0地址转储字节,从而获得访问字节1处的实际数据。这需要时间并因此降低性能。所以将所有数据地址对齐更有效率。

+1

不一定以4字节为一组:这高度依赖于CPU类型。 – jldupont 2010-01-05 13:42:48

+1

这有点简化了:在内存位置有一个字节大小的值不能被4除尽也是可以的。在内存位置可以用2分割的WORD大小的值也可以。 – Niki 2010-01-05 13:43:06

+3

我想要简单一点, - ) – Alon 2010-01-05 13:44:15

3

除了jldupont的答案,一些架构有加载和存储指令(那些用来读/写,并从存储器)上字对齐的边界操作 - 因此,以从内存中加载一个非对齐的字将需要两个加载指令,一个移位指令,然后是一个掩码指令 - 效率更低!

+0

确实读取小于4个字节的类型(bool,short,whatever)总是包含掩码操作,并且如果它不是4byte对齐也是移位指令? – Mat 2010-01-05 13:50:58

+0

@Mat:不一定是“移位指令”:在电路层面,芯片设计师被用来将这种类型的操作称为“字节交换器”。 – jldupont 2010-01-05 13:58:44

6

缓存行是缓存的基本单位。通常它是16-64字节或更多。

Pentium IV:64字节; Pentium Pro/II:32字节; Pentium I:32字节; 486:16字节。

myrandomreader: 
    ; ... 
    ; ten instructions to generate next pseudo-random 
    ; address in ESI from previous address 
    ; ... 
    MOV EAX, DS:[ESI] ; X 
    LOOP myrandomreader 

对于存储器读跨2个高速缓存行:

(为L1高速缓存未命中)的处理器必须等待整个高速缓存行1至从L2-> L1被读入之前,在处理器可以请求第二高速缓存行,从而导致短的执行失速

(用于L2高速缓存未命中)的处理器必须等待两个脉冲串从L3高速缓存(如果存在)或主存储器中读取来完成,而不是一个

处理器档位

  • 随机4字节读取跨越高速缓存行边界的时间约5%为64个字节的超高速缓存行,对于32分字节的人10%和16分字节的人20%。

  • 对于未对齐数据的某些指令,即使它位于缓存行内,也可能存在额外的执行开销。这是在英特尔网站上讨论的一些SSE指令。

  • 如果您自己定义结构,查看将所有< 32位数据字段一起列在struct中以便减少填充开销或者查看打开还是关闭打包效果更好对于特定的结构。

  • 在MIPS和许多其他平台上,您没有得到选择并且必须对齐 - 如果您不需要内核异常!

  • 比对也不管额外特意给你,如果你是在公共汽车上做I/O或使用原子操作,如原子递增/递减或者如果你希望能够给你的代码移植到非Intel。

  • 在只有英特尔(!)代码上,通常的做法是为网络和磁盘定义一组打包结构,并为内存中定义一组填充结构,并在这些格式之间转换数据的例程磁盘和网络格式的“endianness”)。