我理解发生在结构成员之间的填充以确保各个类型的正确对齐。但是,为什么数据结构必须是最大成员的多倍?我不明白最后需要填充。为什么结构的大小需要是任何结构成员的最大对齐的倍数
参考: http://en.wikipedia.org/wiki/Data_structure_alignment
我理解发生在结构成员之间的填充以确保各个类型的正确对齐。但是,为什么数据结构必须是最大成员的多倍?我不明白最后需要填充。为什么结构的大小需要是任何结构成员的最大对齐的倍数
参考: http://en.wikipedia.org/wiki/Data_structure_alignment
好问题。考虑这个假设类型:
struct A {
int n;
bool flag;
};
所以,A
类型的对象,应该采取五个字节(四为INT加上一个用于布尔),但实际上它需要八。为什么?如果使用这样的类型
答案是看出:
const size_t N = 100;
A a[N];
如果每个A
只有五个字节,然后a[0]
会保持一致,但a[1]
,a[2]
和大多数其他元素不会。
但为什么对齐甚至重要?有几个原因,所有硬件相关。一个原因是最近/经常使用的内存被缓存在CPU芯片上的高速缓存行中以用于快速访问。比缓存行更小的对齐对象总是适合单行(但请参阅下面附加的有趣注释),但未对齐的对象可能跨越两行,浪费缓存。
实际上甚至有更基本的硬件原因,与字节可寻址数据在32位或64位数据总线上传输的方式有关,这与缓存行很相似。不仅如此,错位会阻塞总线,导致额外的提取(由于像以前一样跨越),但它也会强制寄存器在进入时转移字节。更糟糕的是,错位容易导致优化逻辑混淆(至少,英特尔的优化手册说它的确如此,尽管我对这最后一点没有个人认识)。所以,从性能的角度来看,错位是非常糟糕的。
由于这些原因,通常浪费填充字节是值得的。
更新:下面的评论都是有用的。我推荐他们。
由于虚拟寻址的。
“......一个页面大小的边界上对齐页通过在地址替换 较高位,而不是做复杂的算术让 硬件映射虚拟地址到物理地址。”
顺便说一句,我发现维基百科页面写得很好。
不相关.... – 2012-04-25 07:11:27
如果CPU的寄存器大小是32位,那么它可以通过单个汇编指令获取位于32位边界的存储器。抓取32位比较慢,然后得到从第8位开始的字节。
顺便说一句:不需要填充。你可以问结构是否包装。
包装是特定于编译器的,而不是语言。如果处理未对齐的加载和存储的CPU /内核支持不存在或未打开,包装将在RISC机器上爆炸。只为速度对准;对某些机器来说这是一个很难的要求。 – Kaz 2012-04-25 04:36:55
根据硬件的不同,对齐可能是必要的,或者只是帮助加快执行速度。
有一定数量的处理器(我相信ARM),其中未对齐的访问会导致硬件异常。干净利落。
尽管典型的x86处理器更宽松,但在访问未对齐的基本类型时仍存在一定的代价,因为处理器在能够操作它之前必须做更多的工作才能将这些位带入寄存器。尽管如此,编译器通常会提供特定的属性/编译指示,但仍需要包装。
*小于缓存行的对齐对象始终适合单行,但未对齐对象可能跨过两行*> **否**。是否对齐可能跨越两条线。 – 2012-04-25 07:10:40
@MatthieuM。,实际上是和不。高速缓存行大小是最大数据大小和任何其他基本类型的倍数。因此,所有(呃,大多数)对齐的_native_类型自然会在单个缓存行内。考虑任何1,2,4,8,16字节对齐的类型将自动对齐,以适应64或128字节的高速缓存行。如果情况并非如此,系统本质上将不可用。 – 2012-04-25 07:25:19
@ edA-qamort-ora-y:当然,但是一个基本类型复合的对象可能很容易跨越两条线。即使它更小。假设一个64字节的行,我可以有一个大小为48字节的对象,并且在这样的对象的表中,至少有两个会跨越两个缓存行。 – 2012-04-25 08:17:30