“double”类型需要进行8字节对齐吗?

5
我理解字对齐,这使得CPU只需要在将整数读入寄存器时读取一次。
但是对于“double”来说,8字节对齐(假设为32位系统)是否必要?这有什么好处?如果用于存储“double”的空间只有4字节对齐会发生什么?
5个回答

11

多个硬件组件可能会受到未对齐的负载或存储的影响。

  • 内存接口可能是8字节宽的,并且只能在8字节的倍数处访问内存。加载未对齐的8字节双精度数需要通过总线进行两次读取。存储则更糟,因为对齐的8字节存储可以直接写入8字节的内存,而未对齐的8字节存储必须读取两个8字节块,将新数据与旧数据合并,然后写入两个8字节块。
  • 缓存行通常为32或64字节。如果8字节对象对齐到8字节的倍数,则每个对象仅位于一个缓存行中。如果未对齐,则某些对象部分位于一个缓存行中,而部分位于另一个缓存行中。然后加载或存储这些对象需要使用两个缓存行而不是一个。此效果在所有缓存级别(现代处理器中三个级别很常见)中都发生。
  • 内存系统页面通常为512字节或更大。同样,每个对齐的对象仅位于一个页面中,但某些未对齐的对象位于多个页面中。访问每个页面都需要硬件资源:虚拟地址必须转换为物理地址,这可能需要访问翻译表,并且必须检测地址冲突。(处理器可以同时执行多个加载和存储操作。即使您的程序看起来是单线程的,处理器也会提前读取指令并尝试执行其中可执行的指令。因此,处理器可能在前面的指令完成之前开始执行加载指令。但是,为确保这不会导致错误,处理器会检查每个加载指令以确保它没有从前面的存储指令正在更改的地址进行加载。如果访问跨越页面边界,则必须分别检查所加载数据的两个部分。)

系统对未对齐操作的响应因系统而异。某些系统仅支持对齐访问。在这种情况下,未对齐访问会导致异常,导致程序终止或引发执行特殊处理程序的异常,该处理程序通过执行对齐操作并根据需要合并数据来模拟未对齐操作。此类软件处理程序比硬件操作慢得多。

一些系统支持非对齐访问,但通常会消耗比对齐访问更多的硬件资源。在最好的情况下,硬件会执行两个操作而不是一个操作。但是,有些硬件被设计成在开始操作时像对齐操作一样,然后在发现操作不对齐时中止它,并使用硬件中的不同路径重新开始处理非对齐操作。在这种系统中,非对齐访问具有显着的性能惩罚,尽管它不像软件处理非对齐访问的系统那样大。
在某些系统中,硬件可能具有多个负载存储执行单元,可以像一个单元执行对齐访问的操作一样快速地执行非对齐访问所需的两个操作。因此,非对齐访问没有直接的性能降低。然而,由于多个执行单元被非对齐访问占用,它们无法执行其他操作。因此,通常并行执行许多负载存储操作的程序,在非对齐访问下将比对齐访问执行得更慢。

3
在许多架构中,任何负载/存储单元(short、int、long)的非对齐访问都会引发异常。编译器负责确保在可能存在对齐不良的数据上不会发生这种情况,通过发出较小的访问指令并在寄存器中重新组装,如果不能证明给定指针是OK的话。
从性能的角度来看,在32位系统上将double 8字节对齐可能有几个好处。最明显的是,8字节double的4字节对齐意味着一个元素可能跨越两个高速缓存行的边界。内存访问以整个高速缓存行为单位进行,因此对齐不良会使访问成本翻倍。

1
  • 我记得对于486处理器的建议是将双精度数据对齐到32位边界,因此要求64位对齐不是强制性的。

  • 你似乎认为数据总线宽度和处理器位数之间有关系。虽然通常情况下是这样,但两者都可以存在变化。例如,奔腾处理器是32位处理器,但其数据总线大小为64位。

  • 缓存提供了其他东西,这可能解释了为什么对于64位类型具有64位对齐的有用性。在这里,外部总线不是因素,重要的是缓存行大小。跨越缓存行的数据访问成本比未跨越它的数据访问成本更高(即使在两种情况下都不对齐)。按照类型的大小进行对齐确保它们不会跨越缓存行,只要缓存行大小是类型大小的倍数。


1
对于80x86,对齐仅是为了提高性能的建议;实际上并不需要对齐(直到您开始查看SSE,其中一些指令需要16字节对齐)。 - Brendan

0

编辑:

字节对齐的优点在于减少检索数据所需的内存周期数。例如,如果8个字节对齐,则可能只需要一个周期,而现在可能需要2个周期,因为第一次获取了其中的一部分,下一个内存周期中获取其余部分。

我发现这个: “对齐访问更快,因为到内存的外部总线不是单字节宽-通常是4或8字节宽(甚至更宽)。因此,CPU不会一次获取单个字节-它获取从请求地址开始的4或8个字节。因此,内存地址的2或3个最低有效位实际上并未由CPU发送-外部内存只能在总线宽度的倍数地址处读取或写入。如果您请求地址“9”处的字节,则CPU实际上会要求内存提供从地址8开始的字节块,并将第二个字节加载到您的寄存器中(丢弃其他字节)。

这意味着未对齐的访问可能需要从内存中读取两次:如果您请求从地址9开始的8个字节,CPU 必须获取从地址8开始的8个字节以及从地址16开始的8个字节,然后屏蔽您想要的字节。另一方面,如果您请求从地址8开始的8个字节,则只需要进行一次获取。有些 CPU 甚至不会执行此类未对齐的加载 - 它们将仅引发异常(甚至默默地加载错误的数据!)。 您可以查看此链接以获取更多详细信息。http://www.ibm.com/developerworks/library/pa-dalign/

在64位系统中,是的,双精度浮点数的8字节对齐只需要一次内存读取。但在32位系统中,双精度浮点数进行8字节对齐有什么好处呢? - Infinite

0

我刚刚找到了答案:

"6. 当在32位机器上进行内存读取时,以4字节为单位的读取效率很高,那么为什么双精度类型应该对齐在8字节边界上呢?

需要注意的是,大多数处理器都有数学协处理器,称为浮点运算单元(FPU)。代码中的任何浮点操作都将被转换为FPU指令。主处理器与浮点执行无关。所有这些都将在幕后完成。

根据标准,双精度类型将占用8个字节。而且,在FPU中执行的每个浮点操作都将具有64位长度。甚至在执行之前,浮点类型也会被提升为64位。

FPU寄存器的64位长度强制双精度类型分配在8字节边界上。我假设(我没有确切的信息)在FPU操作的情况下,数据获取可能会有所不同,我的意思是数据总线,因为它进入FPU。因此,双精度类型的地址解码将不同(预期在8字节边界上)。这意味着,浮点单位的地址解码电路将没有最后3个引脚。"


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接