【问题标题】:Is 8-byte alignment for "double" type necessary?“双”类型是否需要 8 字节对齐?
【发布时间】:2014-01-19 16:04:48
【问题描述】:

我了解字对齐,这使得 CPU 在将整数读入寄存器时只需读取一次。

但是 8-byte alignment (假设是 32 位系统)对于“双”是必要的吗?有什么好处?如果存储“double”的空间只是 4 字节对齐会发生什么?

【问题讨论】:

    标签: c cpu memory-alignment


    【解决方案1】:

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

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

    系统对未对齐操作的响应因系统而异。一些系统被设计为仅支持对齐的访问。在这些情况下,未对齐访问要么导致导致程序终止的异常,要么导致执行在软件中模拟未对齐操作的特殊处理程序的异常(通过执行对齐操作并根据需要合并数据)。诸如此类的软件处理程序比硬件操作慢得多。

    一些系统支持非对齐访问,但这通常比对齐访问消耗更多的硬件资源。在最好的情况下,硬件执行两个操作而不是一个。但是有些硬件被设计为像对齐一样开始操作,然后在发现操作未对齐时中止它并使用硬件中的不同路径重新开始处理未对齐的操作。在此类系统中,未对齐的访问会显着降低性能,尽管它不如在软件处理未对齐访问的系统中那么严重。

    在某些系统中,硬件可能具有多个加载存储执行单元,它们可以执行非对齐访问所需的两项操作,就像一个单元执行对齐访问的操作一样快。因此,未对齐访问不会直接降低性能。然而,由于多个执行单元被非对齐访问保持忙碌,它们无法执行其他操作。因此,执行许多加载-存储操作(通常是并行的)的程序在使用非对齐访问时会比使用对齐访问时执行得更慢。

    【讨论】:

      【解决方案2】:

      在许多架构上,任何加载/存储单元(short、int、long)的非对齐访问只是一个例外。编译器负责确保它不会发生在可能未对齐的数据上,如果他们不能证明给定的指针是正确的,他们会发出较小的访问指令并在寄存器中重新组装。

      在性能方面,32 位系统上双精度的 8 字节对齐可能很有价值,原因有几个。最明显的是 8 字节双精度的 4 字节对齐意味着一个元素可以跨越两个高速缓存行的边界。内存访问以整个高速缓存行为单位进行,因此未对齐会使访问成本加倍。

      【讨论】:

        【解决方案3】:

        我刚刚找到the answer:

        "6. 当内存读取在 32 位机器上一次读取 4 个字节时高效,为什么要在 8 字节边界上对齐双精度类型?

        请务必注意,大多数处理器都具有数学协处理器,称为浮点单元 (FPU)。代码中的任何浮点运算都会被翻译成 FPU 指令。主处理器与浮点执行无关。所有这些都将在幕后完成。

        按照标准,双精度类型将占用 8 个字节。而且,在 FPU 中执行的每个浮点运算都是 64 位长度的。甚至浮点类型在执行前也会被提升为 64 位。

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

        【讨论】:

          【解决方案4】:

          编辑:

          字节对齐的优点是减少了检索数据的内存周期数。例如,一个 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 字节对齐对双精度有什么好处?
          【解决方案5】:
          • 我似乎记得 486 的建议是在 32 位边界上对齐双精度,因此不需要 64 位对齐。

          • 您似乎认为数据总线宽度和处理器位数之间存在关系。虽然经常发生这种情况,但您可以在两个方向上找到变化。例如 Pentium 是 32 位处理器,但其数据总线大小为 64 位。

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

          【讨论】:

          • 对于 80x86,对齐只是提高性能的建议;并且实际上不需要对齐(直到您开始查看某些指令需要 16 字节对齐的 SSE)。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2014-09-21
          • 2011-01-23
          • 2013-11-24
          • 2017-04-19
          • 1970-01-01
          • 1970-01-01
          • 2021-09-25
          相关资源
          最近更新 更多