【问题标题】:Memory alignment on modern processors?现代处理器上的内存对齐?
【发布时间】:2010-12-23 18:37:57
【问题描述】:

我经常看到如下代码,例如,在内存中表示一个大的位图:

size_t width = 1280;
size_t height = 800;
size_t bytesPerPixel = 3;
size_t bytewidth = ((width * bytesPerPixel) + 3) & ~3; /* Aligned to 4 bytes */
uint8_t *pixelData = malloc(bytewidth * height);

(即,分配为连续内存块的位图,bytewidth 与一定数量的字节对齐,最常见的是 4。)

然后通过以下方式给出图像上的一个点:

pixelData + (bytewidth * y) + (bytesPerPixel * x)

这引出了两个问题:

  1. 像这样对齐缓冲区是否会对现代处理器的性能产生影响?我应该担心对齐,还是编译器会处理这个问题?
  2. 如果确实有影响,有人可以指点我一个资源来找到各种处理器的理想字节对齐方式吗?

谢谢。

【问题讨论】:

    标签: c performance memory memory-management alignment


    【解决方案1】:

    这取决于很多因素。如果您一次只访问一个字节的像素数据,则在绝大多数情况下对齐不会产生任何影响。对于读取/写入一个字节的数据,大多数处理器根本不关心该字节是否在 4 字节边界上。

    但是,如果您以大于一个字节的单位访问数据(例如,以 2 字节或 4 字节为单位),那么您肯定会看到对齐效果。对于某些处理器(例如许多 RISC 处理器),在某些级别访问未对齐的数据是完全非法的:尝试从非 4 字节对齐的地址读取 4 字节字将产生数据访问异常(或数据存储异常) ) 例如,在 PowerPC 上。

    在其他处理器(例如 x86)上,允许访问未对齐的地址,但它通常会带来隐藏的性能损失。内存加载/存储通常在微码中实现,微码会检测未对齐的访问。通常,微码会从内存中获取正确的 4 字节数量,但如果未对齐,则必须从内存中获取 两个 4 字节位置,并从两个位置的适当字节。获取两个内存位置显然比一个慢。

    不过,这只是用于简单的加载和存储。某些指令,例如 MMX 或 SSE 指令集中的指令,要求它们的内存操作数正确对齐。如果您尝试使用这些特殊指令访问未对齐的内存,您会看到类似非法指令异常的情况。

    总而言之,除非您正在编写超级性能关键代码(例如在汇编中),否则我不会太担心对齐问题。编译器可以帮助您很多,例如通过填充结构使 4 字节数量在 4 字节边界上对齐,并且在 x86 上,CPU 还可以帮助您处理未对齐的访问。由于您处理的像素数据为 3 个字节,因此您几乎总是在进行单字节访问。

    如果您决定改为以单个 4 字节访问(而不是 3 个 1 字节访问)来访问像素,最好使用 32 位像素并将每个单独的像素对齐在 4 字节上边界。将每一行与 4 字节边界对齐,但不是每个像素都将产生很小的影响(如果有的话)。

    根据您的代码,我猜它与读取 Windows 位图文件格式有关——位图文件要求每个扫描线的长度为 4 字节的倍数,因此使用该属性设置像素数据缓冲区有您可以一口气读入整个位图的属性(当然,您仍然必须处理扫描线是从下到上而不是从上到下存储的事实,并且像素数据是 BGR 而不是 RGB)。不过,这并不是一个真正的优势——在位图中一次读取一条扫描线并不难。

    【讨论】:

      【解决方案2】:

      是的,对齐确实会对现代处理器(比如 x86)产生性能影响。通常,数据的加载和存储发生在自然对齐边界上;如果您将 32 位值放入寄存器,如果它已经在 32 位边界上对齐,它将是最快的。如果不是,x86 将“为你处理”,从某种意义上说,CPU 仍然会执行负载,但它会花费大量的周期来完成它,因为会有内部争吵“重新对齐”访问权限。

      当然,在大多数情况下,这种开销是微不足道的。二进制数据的结构经常以不对齐的方式打包在一起,以便通过网络传输或持久保存在磁盘上,打包存储的大小优势超过了偶尔对这些数据进行操作所带来的任何性能损失。

      但特别是对于随机访问的大型统一数据缓冲区以及聚合性能确实很重要的情况,例如上面的像素缓冲区,保持数据结构对齐仍然是有益的。

      请注意,在您上面给出的示例中,只有像素数据的每一“行”是对齐的。像素本身仍然是 3 个字节长,并且通常在“行”内未对齐,因此这里没有太多好处。例如,有些纹理格式每个像素有 3 个字节的实际数据,实际上只是在每个像素上浪费了一个额外的字节来保持数据对齐。

      这里有一些更一般的信息:http://en.wikipedia.org/wiki/Data_structure_alignment

      (架构之间的具体特征各不相同,包括自然对齐是什么,CPU 是否自动处理未对齐的加载/存储,以及最终的成本有多大。如果 CPU 不能神奇地处理访问,通常编译器/C 运行时会尽其所能为您完成这项工作。)

      【讨论】:

        【解决方案3】:

        缓冲区对齐有影响。问题是:它是否有重大影响?答案可以是高度application specific。在本身不支持非对齐访问的架构中——例如,68000 和 68010(68020 增加了非对齐访问)——这确实是一个性能和/或维护问题,因为 CPU 将出现故障,或者可能陷入处理程序以执行非对齐访问.

        可以估计各种处理器的理想对齐方式:4 字节对齐适用于具有 32 位数据路径的架构。 64 位的 8 字节对齐。但是,L1 caching has an effect。对于许多 CPU 来说,这是 64 字节,尽管它在未来无疑会改变。

        对齐太高(即 8 个字节,而只需要 2 个字节)不会导致任何较窄系统的性能低下,即使在 8 位微控制器上也是如此。它只是浪费(可能)几个字节的存储空间。

        您的示例相当奇特:3 字节元素有 50% 的机会单独未对齐(到 32 位),因此对齐缓冲区似乎毫无意义——至少出于性能原因。但是,在对整个事物进行批量传输的情况下,它会优化第一次访问。请注意,未对齐的第一个字节也可能会对传输到视频控制器的性能产生影响。

        【讨论】:

          【解决方案4】:
          • 像这样对齐缓冲区是否会对现代处理器的性能产生影响?

          是的。例如,如果 memcpy 使用 SIMD 指令(如 MMX/SSE)进行了优化,则对齐内存的某些操作会更快。在某些架构中,如果数据未对齐,则(处理器)指令会失败,因此某些东西可能在您的机器上工作,但在另一台机器上却不行。

          通过对齐数据,您还可以更好地利用 CPU 缓存。

          • 我应该完全担心对齐,还是编译器会处理这个问题?

          当我使用动态内存并且编译器无法处理时,我应该担心对齐问题(请参阅对此评论的回复)。

          对于代码中的其他内容,您可以使用 -malign 标志和对齐属性。

          【讨论】:

          • -malign 与堆栈和代码对齐有关,这里不相关。内存分配有一个malloc,它产生一个连续的块。如果行长度width*bytesPerPixel 不能被 4 整除(或本机字大小、SIMD 寄存器或高速缓存行,具体取决于应用程序),则对许多行的访问将不对齐。上面的对齐有效地使每一行比必要的稍长,以便它们都对齐。编译器无法进行此优化。但在这个例子中,额外的对齐是无操作的,因为1280*3 % 256 = 0
          • 我知道-malign。我说的是总体上的对齐方式。
          猜你喜欢
          • 2010-11-06
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-05-26
          • 1970-01-01
          • 2017-07-11
          • 2017-03-19
          • 1970-01-01
          相关资源
          最近更新 更多