浮点数数组是否总是按照16字节边界对齐?

4

我的理解是,如果您希望对齐数组,必须明确指定其对齐方式。

然而,我声明的浮点数组似乎总是对齐到16个字节。

float *ptr1 = new float[1];
cout<<"ptr1: "<<ptr1<<endl;
float *ptr2 = new float[3];
cout<<"ptr2: "<<ptr2<<endl;
float arr1[7];
cout<<"arr1: "<<arr1<<endl;
float arr2[9] __attribute__((aligned(2)));
cout<<"arr2: "<<arr2<<endl;

这里是输出结果

ptr1: 0x13dc010
ptr2: 0x13dc030
arr1: 0x7fff874885c0
arr2: 0x7fff87488590

这是有原因的吗?我正在使用gcc 4.6.3。

但是,如果它是指向float位置或静态分配的指针,我就看不到它了。

static float arr3[9] __attribute__((aligned(2)));
cout<<"arr3: "<<arr3<<endl;
float *x;
cout<<"x: "<<x<<endl;

输出:

arr3: 0x4030b2
x: 0x7fff8c7dd9e8

这段代码是在x64架构上运行的。


1
这取决于你的编译器。查看它们的文档。另外,你的 float *x 从未被初始化,因此其结果基本上是随机的。 - Mark Ransom
我的gcc 4.8.1默认只对齐到8个字节。这是因为malloc实现了这一点(...这也是operator new调用的原因)。总之,不要指望它。 - Damon
3个回答

6

对齐要求由每个编译器确定,受硬件要求和任何相关的ABI影响。

C和C++语言讨论类型的对齐,但它们不会强制执行任何特定的要求(除了结构体的对齐至少是其任何成员的对齐)。有效的实现可以允许所有数据类型都是字节对齐的,或者它可以要求每个标量类型对齐到其自身的大小(后者更常见)。中间对齐是可能的,例如将8字节类型对齐到4字节边界上。

特别是在x86上,将标量对齐到其大小使得访问更加高效,但是未对齐的访问仍然可以正确工作,尽管速度稍慢。

数组的float必需对齐方式与单个float对象的所需对齐方式相同。如果float为4字节,则该对齐方式不能大于4字节,因为数组在其元素之间没有间隙。

某个编译器可能会选择对数组对象施加更严格的对齐方式,如您(可能)看到的,如果这使得对这些对象的访问更加高效。

如果new操作符通过调用malloc实现,那么所有由new分配的对象都将具有足够严格的对齐方式以适用于任何类型。
如果float数组总是对齐到16字节边界,那是因为编译器选择以这种方式分配它们,而不是语言要求。另一方面,如果使用别名来强制将float数组对齐到4字节(假设sizeof(float)==4),则对该数组及其元素的访问仍应正常工作。
顺便说一下,当我在我的x86_64系统上运行您的代码(在包含main程序后)时,我得到类似于您的结果。当我在x86系统上运行它时,我得到:
ptr1: 0x9e34008
ptr2: 0x9e34018
arr1: 0xbfefa160
arr2: 0xbfefa17c

我在两个系统下都使用Linux上的gcc。
因此,对于你的问题的直接答案是否定的,float数组并不总是对齐到16字节边界。
但在大多数情况下,你没有特别关心的理由。除非你玩别名技巧(把某个声明类型的对象视为另一个类型的对象),否则编译器将始终为每个对象提供至少所需的对齐方式以进行正确访问。

我在我的项目中使用了一些SSE。因此,我想我应该明确指定对齐到16字节,而不是依赖于编译器为我完成。我在x64上运行它。感谢您的回复。 - Mathai
我自己没有使用过SSE;我不知道在这种情况下指定所需对齐方式的正确方法是什么。如果你问如何使用SSE而不是一个更通用的关于对齐的问题,你可能会得到更有用的信息。询问你实际要解决的问题。而且,通过谷歌搜索“SSE对齐”可以得到很多结果;我会让你自己查看。 - Keith Thompson
我已经运行了代码,只是想确保必须显式指定对齐方式。SSE浮点操作需要16字节对齐。我的项目中有一个性能关键部分。 - Mathai
@Mathai:它可能只是巧合地工作。您需要咨询一些熟悉SSE和gcc相互作用或实际文档的人的建议。我回答了您提出的问题; 您需要进行更多研究,可能会发布一个新问题,专门涉及SSE和对齐问题。 - Keith Thompson
当你声明类中的字段时,可能需要关注对齐方式。不正确的顺序可能导致比所需更大的对象。我们说的是30%以上的增加。对于大多数小程序来说可能无关紧要,但如果你有数百万个该类型的对象,浪费的空间会累积起来。此外,执行某些操作(如复制)将需要更长的时间。如果你要进行数百万次复制,这可能会减慢速度。但你是对的,你不必考虑它,只需编写代码以使其与预期结果编译即可。 - user904963

1
我不确定我完全理解这个问题,但如果你想要更大的尺寸,我建议使用类型“double”而不是“float”。 “Float”通常有4个字节(约7个数字)的大小限制,而“double”通常有8个字节(约15个数字)的大小限制。

编辑:抱歉,我误解了问题


0

这取决于平台上最严格的基本类型对齐要求,例如长双精度浮点数,在我的 Debian 64 系统中需要 16 字节对齐。

这也可能会影响堆栈帧的对齐方式。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接