Yak*_*kov 8 c c++ structure memory-alignment
根据这篇文章的讨论,我理解结构成员对齐的主要原因是性能(以及一些架构限制).
如果我们将在编译32位x86时调查Microsoft(Visual C++),Borland/CodeGear(C++ - Builder),Digital Mars(DMC)和GNU(GCC):对齐为int4个字节,如果int未对齐,则可能会发生2行存储体读取.
我的问题是为什么不double将4字节对齐呢?4个字节对齐double也会导致2行内存库读取....
例如,在以下示例中,由于double是8对齐的,因此结构的实际大小将是sizeof(char) + (alignment for double padding) + sizeof(int) = 20 bytes.
typedef struct structc_tag{
char c;
double d;
int s;
} structc_t;
Run Code Online (Sandbox Code Playgroud)
谢谢
延伸评论:
根据海湾合作委员会的文件-malign-double:
double在双字边界上对齐变量会产生代码,这些代码在奔腾上运行得更快,代价是更多的内存.在x86-64上,
-malign-double默认情况下启用.警告:如果使用该
-malign-double开关,则包含上述类型的结构的排列方式与386的已发布应用程序二进制接口规范的排列方式不同,并且与未使用该开关编译的代码中的结构不兼容.
这里的一个词是指32位的i386字.
double即使在32位模式下,Windows也使用64位对齐值,而符合SysV i386 ABI标准的Unices使用32位对齐.32位Windows API Win32来自Windows NT 3.1,与当前一代Windows版本不同,它针对的是英特尔i386,Alpha,MIPS甚至是不起眼的英特尔i860.由于像Alpha和MIPS这样的原生RISC系统要求double值为64位对齐(否则会发生硬件故障),因此可移植性可能是Win32 i386 ABI中64位对齐背后的基本原理.
64位x86系统,也称为AMD64或x86-64或x64,要求double值为64位对齐,否则会发生错位故障,硬件会进行昂贵的"修复",这会大大减慢内存访问速度.这就是为什么double在所有现代x86-64 ABI(SysV和Win32)中值都是64位对齐的原因.
大多数编译器会自动将数据值与平台的字大小或数据类型的大小对齐,以较小者为准.绝大多数消费者和企业处理器使用32位字长.(即使64位系统通常使用32位作为本机字大小)
因此,结构中成员的排序可能会浪费一些内存.在你的具体情况下,你很好.我将在评论中添加已用内存的实际占用空间:
typedef struct structc_tag{
char c; // 1 byte
// 3 bytes (padding)
double d; // 8 bytes
int s; // 4 bytes
} structc_t; // total: 16 bytes
Run Code Online (Sandbox Code Playgroud)
此规则也适用于结构,因此即使您重新排列它们以使最小的字段最后,您仍然会有相同大小的结构(16字节).
typedef struct structc_tag{
double d; // 8 bytes
int s; // 4 bytes
char c; // 1 byte
// 3 bytes (padding)
} structc_t; // total: 16 bytes
Run Code Online (Sandbox Code Playgroud)
如果要声明更多小于4个字节的字段,如果按大小将它们组合在一起,则可以看到一些内存减少.例如:
typedef struct structc_tag{
double d1; // 8 bytes
double d2; // 8 bytes
double d3; // 8 bytes
int s1; // 4 bytes
int s2; // 4 bytes
int s3; // 4 bytes
short s4; // 2 bytes
short s5; // 2 bytes
short s6; // 2 bytes
char c1; // 1 byte
char c2; // 1 byte
char c3; // 1 byte
// 3 bytes (padding)
} structc_t; // total: 48 bytes
Run Code Online (Sandbox Code Playgroud)
声明一个愚蠢的结构可能会浪费大量的内存,除非编译器重新排序你的元素(一般情况下,它不会被明确告知)
typedef struct structc_tag{
int s1; // 4 bytes
char c1; // 1 byte
// 3 bytes (padding)
int s2; // 4 bytes
char c2; // 1 byte
// 3 bytes (padding)
int s3; // 4 bytes
char c3; // 1 byte
// 3 bytes (padding)
} structc_t; // total: 24 bytes
// (9 bytes wasted, or 38%)
// (optimal size: 16 bytes (1 byte wasted))
Run Code Online (Sandbox Code Playgroud)
双精度大于32位,因此根据第一节中的规则,32位对齐.有人提到了一个更改对齐的编译器选项,并且默认编译器选项在32位和64位系统之间是不同的,这也是有效的.所以关于双打的真正答案是它取决于平台和编译器.
内存性能由单词控制:从内存加载发生在依赖于数据放置的阶段.如果数据覆盖一个单词(即单词对齐),则只需加载该单词.如果未正确对齐(即0x2处的int),则处理器必须加载2个字才能正确读取其值.这同样适用于双打,通常占用2个字,但如果未对齐,则占用3.在64位系统上,可能有64位数量的本机加载,它们在32位系统上的行为类似32位,如果正确对齐,它们可以一次装载,但除此之外,它们将需要2.
首先,它是强加对齐要求的体系结构,有些会容忍未对齐的内存访问,有些则不会。
我们以 x86-32bitwindows平台为例,在该平台中,int和 的对齐要求分别为long和。4 bytes8 bytes
很清楚为什么int需要对齐4 bytes,只是为了让CPU只能通过一次访问来读取所有内容。
之所以需要doulbeis8 bytes和 not的对齐要求4 bytes,是因为如果是的话,4 bytes想想如果这个 double 位于地址60并且缓存行大小为 ,会发生什么64bits,在这种情况下,处理器需要从内存加载 2 个缓存行。缓存,但如果这double是对齐的,则不会发生这种情况,因为在这种情况下,它将double始终是一个缓存行的一部分,而不是划分在两个缓存行之间。
...58 59|60 61 62 63 64 65 66 67|68 69 70 71...
- - - - - - - - - - - - - - - - -
----------+ + + + . . + + + +--------------
| . . |
----------+ + + + . . + + + +--------------
. .
Cache Line 1 . . Cache Line 2
- - - - - - - - - - - - - - - - -
Run Code Online (Sandbox Code Playgroud)