我理解存在数据结构alignment restrictions以优化内存访问,因为现代CPU以字大小(或多个字大小)块来获取内存。这将使我认为对齐数据的最佳方式是(固定)字边界。
例如,考虑32位机器上的以下结构(使用gcc v6.2.0编译; CFLAGS:-Wall -g -std = c99 -pedantic):
struct layoutA {
char a; /* start: 0; end: 1; padding: 3 */
uint32 b; /* start: 4; end: 8; padding: 0 */
uint64 c; /* start: 8; end: 16; padding: 0 */
};
/* sizeof(struct layoutA) = 16 */
struct layoutB {
uint32 b; /* start: 0; end: 4; padding: 4 */
uint64 c; /* start: 8; end: 16; padding: 0 */
char a; /* start: 16; end: 0; padding: 3 */
};
/* sizeof(struct layoutB) = 24 */
由于自对齐限制,c
强制第二个结构将自身对齐到8字节边界而不是字边界(4字节)。
这如何与最初的对齐原因 - 内存优化相协调。看起来将c
置于4还应该有助于CPU在2次访问中读取它(类似于需要访问2个字(8和12)以获得整个双字的当前情况。
自对齐如何优化内存访问?换句话说,我们在第二种情况下获得了什么好处,以证明由于自我对齐而失去空间?
答案 0 :(得分:3)
对齐是特定于实现的。它的主要目的不是优化:在某些体系结构上,字访问必须对齐或者它们调用未定义的行为。
在英特尔架构上,大多数未对齐的访问可以配置为正常工作,但程序员不应该依赖它,编译器肯定不会。当支持未对齐访问时,它们通常比对齐访问慢,因此优化效果。
如果类型uint64_t
碰巧需要自我对齐,就像目标系统上的情况一样,struct layoutB
的布局使用的内存比struct layoutA
多,但两者都需要64位对齐。
我们从自我对齐中获得的好处是代码正确性。在不需要64位整数变量自对齐的32位架构上,它是可选的,但你仍然可以获得优势,因为这两个32位部分都来自同一个缓存行。
您可以使用打包属性或编译指示强制执行特定布局并运行基准来评估对目标系统的影响。它很棘手,可能会也可能不会产生差异。
结论:对齐是实现定义的,应留给编译器,但仔细排序结构成员可能会产生更好的内存使用量并大大节省大型结构数组。