我经常看到以下代码,例如,在内存中表示一个大位图:
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)
这引出了两个问题:
感谢。
答案 0 :(得分: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)。然而,这并不是一个很大的优势 - 一次扫描一行扫描线的位图并不是那么难。
答案 1 :(得分:4)
是的,对齐确实对现代产生了性能影响 - 比方说x86 - 处理器。通常,数据的加载和存储发生在自然对齐边界上;如果你在一个寄存器中得到一个32位的值,如果它已经在一个32位的边界上对齐,它将会是最快的。如果不是,那么x86将“为你照顾它”,因为CPU仍将负载,但是它需要更多的周期来完成它,因为将会有内部纠缠“重新调整“访问权限。
当然,在大多数情况下,这种开销很小。二进制数据的结构经常以未对齐的方式打包在一起,以便通过网络传输或磁盘上的持久性,并且打包存储的大小优势超过了偶尔对此数据进行操作的任何打击。
但特别是对于随机访问的统一数据的大缓冲区以及聚合中的性能确实很重要,如上面的像素缓冲区,保持数据结构对齐仍然是有益的。
请注意,对于上面给出的示例,只有像素数据的每个“行”对齐。像素本身仍然是3个字节长,并且通常在“行”内不对齐,因此这里没有太大的好处。例如,有一些纹理格式,每个像素有3个字节的实际数据,而且每个像素都浪费了额外的字节以保持数据对齐。
这里有一些更一般的信息:http://en.wikipedia.org/wiki/Data_structure_alignment
(架构之间的具体特性各不相同,无论是自然对齐,CPU是否自动处理未对齐的加载/存储,以及最终的处理成本如何。在CPU不能神奇地处理访问的情况下,通常,编译器/ C运行时将尽其所能为您完成此工作。)
答案 2 :(得分:1)
是。例如,如果使用SIMD指令(如MMX / SSE)优化memcpy,则对齐内存会使某些操作更快。在某些体系结构中,如果数据未对齐,则(处理器)指令会失败,因此某些内容可能在您的计算机上有效,但在另一台机器上则无效。
使用对齐的数据,您还可以更好地利用CPU缓存。
当我使用动态内存并且编译器无法处理时,我应该担心对齐(请参阅对此评论的回复)。
对于代码中的其他内容,您可以使用-malign标记和align属性来播放。
答案 3 :(得分:1)
缓冲区对齐会产生影响。问题是:它是否会产生重大影响?答案可能很高application specific。在本身不支持未对齐访问的体系结构中 - 例如,68000和68010(68020添加了未对齐访问) - 这是真正的性能和/或维护问题,因为CPU会出错,或者可能陷入处理程序以执行未对齐访问
可以估算各种处理器的理想对齐方式:4字节对齐适用于具有32位数据路径的架构。 64位的8字节对齐。但是,L1 caching has an effect。对于许多CPU而言,这是64字节,但将来无疑会发生变化。
对齐太高(即,只需要两个字节的8字节)不会导致任何较窄系统的性能低效,即使在8位微控制器上也是如此。它只是浪费(可能)几个字节的存储空间。
你的例子相当特殊:3字节元素有50%的可能性单独不对齐(32位),因此对齐缓冲区似乎毫无意义 - 至少出于性能原因。但是,在整体转移的情况下,它优化了第一次访问。请注意,未对齐的第一个字节在传输到视频控制器时也可能会对性能产生影响。