Architecture 对数据对齐感到困惑

Architecture 对数据对齐感到困惑,architecture,x86,x86-64,Architecture,X86,X86 64,我试图弄清楚为什么数据对齐/填充是必要的。来自维基百科: 当现代计算机读取或写入内存地址时,它将以字大小的块进行读取或写入 然而,我可以使用x86的movb指令以字节分辨率清晰地来回移动数据。我在这里遗漏了什么?字对齐内存访问比字节对齐内存访问快得多。这使得传输大数据块的速度更快。您可以寻址单个字节,但很可能会从内存中读取一个字,并在内部缩减为一个字节。这使得访问速度变慢。字对齐内存访问比字节对齐内存访问快得多。这使得传输大数据块的速度更快。您可以寻址单个字节,但很可能会从内存中读取一个字,并在

我试图弄清楚为什么数据对齐/填充是必要的。来自维基百科:

当现代计算机读取或写入内存地址时,它将以字大小的块进行读取或写入


然而,我可以使用x86的movb指令以字节分辨率清晰地来回移动数据。我在这里遗漏了什么?

字对齐内存访问比字节对齐内存访问快得多。这使得传输大数据块的速度更快。您可以寻址单个字节,但很可能会从内存中读取一个字,并在内部缩减为一个字节。这使得访问速度变慢。

字对齐内存访问比字节对齐内存访问快得多。这使得传输大数据块的速度更快。您可以寻址单个字节,但很可能会从内存中读取一个字,并在内部缩减为一个字节。这使得访问速度变慢。

这是一个常见的误解。字节访问不需要对包含该缓存线或内存的32或64位块进行读-修改-写操作,即可进行非缓存访问。看

单字节访问是自动进行的。这意味着与通道的宽度对齐,因此它不会跨越比自身更宽的任何边界

字加载或存储仍然是一个事务,除非它跨缓存线边界拆分,在这种情况下,CPU内部必须访问两个缓存线的相关部分。因此,该引用仅适用于机器字大小的访问。请注意,Intel术语中的字是16位,而不是现代x86 CPU的寄存器或总线宽度。这就是我在前一句中说机器字的原因

因此,在C语言中,将填充添加到结构中并不是因为字节大小的字段的字节访问效率低下,而是因为大于一个字节的对象自然对齐,例如结构中字符后面的int

与字节访问不同,一些相对常见的平台支持或不支持直接未对齐访问,而在那些支持直接未对齐访问的平台上,未对齐访问的效率可能较低,特别是在跨越缓存线时。C编译器将结构视为对其最对齐的成员具有对齐要求。e、 g.int、char和double的结构会有64位对齐,因为有双成员,所以填充使double相对于结构对齐也会绝对对齐,所以结构成员始终保持其自然对齐

即使在没有未对齐的访问惩罚的假设平台上,拥有未对齐的对象也会使依赖原子读写的实现变得非常复杂,因为许多平台保证这些操作的原子性

现代CPU以缓存线大小的块传输数据,而不仅仅是32或64位字。除非您正在访问不可缓存的内存区域,例如设备驱动程序中的内存映射I/O,否则在这种情况下,您将通过外部总线获得实际的字节、16位、32位或64位访问

只要不跨越64位边界,在现代x86 CPU上进行未对齐访问就不会受到任何惩罚。特别是在英特尔,除非您跨越缓存线边界,否则不会对未对齐的加载/存储进行处罚


另请参见tag wiki中的、和性能调整链接。

这是一个常见的误解。字节访问不需要对包含该缓存线或内存的32或64位块进行读-修改-写操作,即可进行非缓存访问。看

单字节访问是自动进行的。这意味着与通道的宽度对齐,因此它不会跨越比自身更宽的任何边界

字加载或存储仍然是一个事务,除非它跨缓存线边界拆分,在这种情况下,CPU内部必须访问两个缓存线的相关部分。因此,该引用仅适用于机器字大小的访问。请注意,Intel术语中的字是16位,而不是现代x86 CPU的寄存器或总线宽度。这就是我在前一句中说机器字的原因

因此,在C语言中,将填充添加到结构中并不是因为字节大小的字段的字节访问效率低下,而是因为大于一个字节的对象自然对齐,例如结构中字符后面的int

与字节访问不同,一些相对常见的平台支持或不支持直接未对齐访问,而在那些支持直接未对齐访问的平台上,未对齐访问的效率可能较低,特别是在跨越缓存线时。C编译器将结构视为对其最对齐的成员具有对齐要求。e、 g.int、char和double的结构会有64位对齐,因为有双成员,所以填充使double相对于结构对齐也会绝对对齐,所以结构成员始终保持其自然对齐

即使在没有未对齐访问惩罚的假设平台上,拥有未对齐对象也会使依赖原子读写的实现变得非常复杂,因为许多平台 我们保证这些操作的原子性

现代CPU以缓存线大小的块传输数据,而不仅仅是32或64位字。除非您正在访问不可缓存的内存区域,例如设备驱动程序中的内存映射I/O,否则在这种情况下,您将通过外部总线获得实际的字节、16位、32位或64位访问

只要不跨越64位边界,在现代x86 CPU上进行未对齐访问就不会受到任何惩罚。特别是在英特尔,除非您跨越缓存线边界,否则不会对未对齐的加载/存储进行处罚


另请参见tag wiki中的,和性能调整链接。

Ah ok-因此,这不是强制计算机在单词块中进行r/w的硬件限制-这是一个优化问题。这很有帮助。谢谢不,单字节加载并不比字加载慢。并且不必访问包含的单词。您可以通过基准测试将[buf+0]复制到[buf+1]:字节加载旁边的字节存储的asm循环来证明这一点。如果加载必须等待存储,则Haswell上的循环将在存储转发延迟上出现瓶颈,约5个周期,因为拷贝使其循环被承载。但是字节负载独立于同一个字内的字节存储,因此循环只会在每个时钟的一个存储上出现瓶颈,而不是每5个时钟的一个。潜在的慢是字访问不对齐,而不是字节访问。啊,好吧,这不是硬件限制迫使计算机在字块中进行r/w,这是一个优化问题。这很有帮助。谢谢不,单字节加载并不比字加载慢。并且不必访问包含的单词。您可以通过基准测试将[buf+0]复制到[buf+1]:字节加载旁边的字节存储的asm循环来证明这一点。如果加载必须等待存储,则Haswell上的循环将在存储转发延迟上出现瓶颈,约5个周期,因为拷贝使其循环被承载。但是字节负载独立于同一个字内的字节存储,因此循环每时钟只在一个存储上出现瓶颈,而不是每5个时钟出现一个。可能较慢的是未对齐的字访问,而不是字节访问。对,那么为什么需要填充呢?也就是说,维基百科上应该出现的真实文本是什么?@BeeOnRope:我在其中的一段末尾有一句话,但你是对的,我应该对它进行扩展。完成。至于Wikipedia,我想只是填充以避免单词元素或对象相对于单词边界的不对齐?我没有看上下文的其余部分,要想弄清楚如何表达一个通用的解释需要做很多工作。是的,基本上为什么需要填充?因为对齐访问有时是唯一直接支持的访问,否则通常效率更高。还要注意的是,只有在最近的英特尔上,只有缓存线分裂才会造成伤害——在其他各种未对齐的访问速度也变慢之前。对齐的另一个原因是线程间相邻元素的独立性。@BeeOnRope:我记不起在Core2和更早版本上进行未对齐整数访问的代价是什么。我知道movdqu很贵,但即使地址对齐,它也很贵。如果你对回答这部分问题有什么好的想法,请对此答案进行编辑;我的回答主要是为了纠正关于字节加载/存储的误解。我尽了最大努力回答了这部分问题:您已经回答了为什么填充会导致自然对齐部分,我只是尝试添加为什么您会关心自然对齐,我认为这就完成了这一点。顺便说一句,我编辑了导致混乱的。文章后面的部分还可以,但是介绍刚刚结束。对,那么为什么需要填充呢?也就是说,维基百科上应该出现的真实文本是什么?@BeeOnRope:我在其中的一段末尾有一句话,但你是对的,我应该对它进行扩展。完成。至于Wikipedia,我想只是填充以避免单词元素或对象相对于单词边界的不对齐?我没有看上下文的其余部分,要想弄清楚如何表达一个通用的解释需要做很多工作。是的,基本上为什么需要填充?因为对齐访问有时是唯一直接支持的访问,否则通常效率更高。还要注意的是,只有在最近的英特尔上,只有缓存线分裂才会造成伤害——在其他各种未对齐的访问速度也变慢之前。对齐的另一个原因是线程间相邻元素的独立性。@BeeOnRope:我记不起在Core2和更早版本上进行未对齐整数访问的代价是什么。我知道movdqu很贵,但即使地址对齐,它也很贵。如果你对回答这部分问题有什么好的想法,请对此答案进行编辑;我的回答主要是为了纠正关于字节加载/存储的误解。我尽了最大努力回答了这部分问题:您已经回答了为什么填充会导致自然对齐 在这一部分,我只是想补充一下为什么你会关心自然对齐,我想这就完成了这一点。顺便说一句,我编辑了导致混乱的。文章后面的部分还可以,但介绍刚刚结束。