现代处理器上的内存对齐?

Mic*_*ael 12 c memory performance memory-management alignment

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

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);
Run Code Online (Sandbox Code Playgroud)

(也就是说,一个位图被分配为一个连续的内存块,它bytewidth与一定数量的字节对齐,最常见的是4.)

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

pixelData + (bytewidth * y) + (bytesPerPixel * x)
Run Code Online (Sandbox Code Playgroud)

这引出了两个问题:

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

谢谢.

Ada*_*eld 7

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

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

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

不过,这仅适用于简单的装载和存储.某些指令(例如MMX或SSE指令集中的指令)要求其存储器操作数正确对齐.如果您尝试使用这些特殊指令访问未对齐的内存,您将看到类似非法指令异常的内容.

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

如果您决定要在单个4字节访问中访问像素(而不是3个1字节访问),那么最好使用32位像素并使每个像素在4字节边界上对齐.将每行对齐到4字节边界但不是每个像素将具有很小的(如果有的话)效果.

基于你的代码,我猜测它与读取Windows位图文件格式有关 - 位图文件要求每个扫描线的长度是4个字节的倍数,因此使用该属性设置像素数据缓冲区具有以下属性:你可以一下子将整个位图读入你的缓冲区(当然,你仍然需要处理扫描线从下到上而不是从上到下存储的事实,并且像素数据是BGR而不是RGB).然而,这并不是一个很大的优势 - 一次扫描一行扫描线的位图并不是那么难.


Ben*_*tto 5

是的,对齐确实会对现代(比如说 x86)处理器产生性能影响。一般来说,数据的加载和存储发生在自然对齐边界上;如果您要将 32 位值存入寄存器,那么如果它已经在 32 位边界上对齐,那么速度将会是最快的。如果不是,x86 将“为您处理”,即 CPU 仍将执行负载,但将需要大量的周期来执行此操作,因为将存在内部争论“重新对齐”访问。

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

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

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

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

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