我们知道,当您这样定义A时,A的大小会有所不同:
class A
{
short a;
double b;
short c;
};或者像这样
class A
{
short a;
short c;
double b;
};我假设我们正在编译32位操作系统,并且我们已经告诉编译器对齐到32位。
编译器真的很难通过重新排序定义来获得最小的大小,同时实现相同的性能吗?
发布于 2013-12-18 16:48:53
这是非常困难的。结构特别需要以与结构定义完全相同的顺序对字段进行排序。
这一要求可能是对Pascal没有这样的要求并造成令人惊讶的结果的反应。
无论如何,并不是所有的CPU架构都需要对齐或填充。在大多数情况下,它会导致轻微的性能损失。在现代处理器时代,由于CPU流水线的其他方面,内存获取中额外的一到两个周期很可能会消失。
发布于 2013-12-18 16:53:13
这对编译器来说并不难,这是标准所禁止的(只有一个例外):第9.2.12节:
分配未使用中间访问说明符声明的(非联合)类的
非静态数据成员,以便后面的成员在类对象中具有更高的地址。未指定由访问说明符分隔的非静态数据成员的分配顺序
例外情况是,具有不同访问修饰符的成员可以被重新排序,因此:
class A
{
public:
int a;
int b;
private:
int c;
int d;
{'\;A和b,不能重新排序。C和d,不能重新排序,但(a和b)可以与wit (c和d)一起重新排序
发布于 2013-12-18 16:54:21
由于标准的要求,编译器无法重新排序(粗略地说:唯一的、递增的地址,以及按访问修饰符分组的C++类)。
这就是为什么重新排序需要手工完成的原因。一般来说,你在这里是幸运的:现有数据类型的更好对齐意味着更小的大小和更好的性能,这里没有冲突。
然而,有时较大的数据(元素)大小意味着更简单的指令。例如,使用位字段来削减几个字节意味着更复杂的代码,代码大小和数据大小之间存在权衡;将内部循环增加500个字节来削减2k数据可能会对代码的优化和内存局部性造成灾难性影响。
编辑工具(如PVS Studio )可以在结构元素顺序不理想且可以改进时发出警告。
edit2对于“为什么这些规则存在”
tl;dr:这很有趣,但并不重要,只是出于好奇心。
首先(毫不留情地复制另一个SO答案)标准的相关部分:
在结构对象中,非位字段成员和位字段所在的单元具有按声明顺序递增的地址。指向经过适当转换的结构对象的指针指向其初始成员(或者,如果该成员是位字段,则指向其所在的单元),反之亦然。在structure对象中可能存在未命名的填充,但在其开始处不存在。
指向标准布局结构对象的指针(使用reinterpret_cast进行适当转换)指向其初始成员(或者,如果该成员是位字段,则指向它所在的单元),反之亦然。注意:因此,在标准布局结构对象中可能有未命名的填充,但不是在其开头,这是实现适当对齐所必需的。-end笔记
(请注意,这与C有关,C++稍微复杂一些。)
该标准很少说明原因,这里的大多数都是“有根据的猜测”:
Distinct adresses是由于标准的其他考虑而产生的要求。
根据现有的编程实践,创建一个指向结构类型转换的指针--相当于指向第一个成员的指针。(通过将“基本结构”嵌套为“派生结构”的第一个成员,它允许在C中实现“数据多态”)
保持程序员指定的顺序的:
“香草猜测”不会破坏手工优化的数据局部性。将最常访问的成员放在顶部可以改善结构中的局部性(允许更好的缓存或更短的寻址指令)。如果编译器重新排序,这些优化可能会丢失,因为总结构大小的增益相对较小。
需要按访问说明符分组的C++:(即“所有公共变量一起,所有受保护变量一起,所有私有变量一起):我从来没有找到这样做的原因(我必须说,这有点令人惊讶)。我可以想象(可能)的目的是允许编译器实现利用硬件访问控制(”这段代码可能不能访问那块内存“)。
https://stackoverflow.com/questions/20653322
复制相似问题