数据对齐:哪里可以读取?可以改变吗?

时间:2011-12-15 00:07:18

标签: c optimization memory

这是关于内存中基元类型的数据对齐的书。

  

Microsoft Windows强加了一个更强的对齐要求 - 任何K字节的原始对象,用于   K = 2,4或8,必须具有K的倍数的地址。特别地,它需要地址   一个double或long long是8的倍数。这个要求增强了内存性能   一些浪费空间的代价。 Linux约定,其中8字节值以4字节对齐   对于i386来说边界可能是好的,当内存稀缺和内存接口时,它就会回来   只有4个字节宽。使用现代处理器,微软的一致性是一个更好的设计决策。数据类型   long double,gcc生成IA32代码,分配12个字节(即使实际数据类型   只需10个字节)对Windows和Linux都有4字节对齐要求。

问题是:

  1. 什么强加数据对齐,操作系统或编译器?
  2. 我可以更改它还是修复它?

3 个答案:

答案 0 :(得分:3)

一般来说,编译器会强制对齐。无论何时声明基本类型(例如double),编译器都会自动将其对齐到堆栈上的8个字节。

此外,内存分配通常也与最大的基本类型对齐,以便您可以安全地执行此操作:

double *ptr = (double*)malloc(size);

无需担心对齐。

因此,一般来说,如果你有良好的习惯编程,你就不必担心对齐。弄错的一种方法是做这样的事情:

char *ch_ptr = (char*)malloc(size);

double *d_ptr = (double*)(ch_ptr + 1);

这个有一些例外:当你开始进入SSE和矢量化时,事情变得有点乱,因为malloc不再保证16字节对齐。


要覆盖某些内容的对齐方式,MSVC会使用declspec(align)修饰符来允许此操作。它习惯于增加某事物的对齐方式。 虽然我不确定它是否允许你减少原始类型的对齐。它明确表示你不能减少与这个修饰符的对齐。


编辑:

我发现文档说明malloc()在GCC上的对齐方式:

  

GNU系统中malloc或realloc返回的块的地址   总是8的倍数(或64位系统上的16)。

来源:http://www.gnu.org/s/hello/manual/libc/Aligned-Memory-Blocks.html

所以是的,GCC现在至少对齐8个字节。

答案 1 :(得分:3)

x86 CPU的对齐要求非常宽松。大多数数据可以在未对齐的位置存储和访问,可能以降低性能为代价。当您开始开发多处理器软件时,事情变得更加复杂,因为对齐对于原子性和观察到的事件顺序变得很重要(从内存写入,这可能不完全正确)。

编译器通常可以指向与默认对齐方式不同的变量。有针对该特定编译器和特殊编译器特定关键字的编译器选项(例如#pragma pack和其他)。

既不能由应用程序员(操作系统已经编译),也不能由操作系统开发人员更改成熟的OS API(当然,除非它们能够破坏兼容性)。

所以,你可以改变一些事情,但不是一切。

答案 2 :(得分:0)

我不知道微软从哪里获取信息,但结果如何 gcc(4.6.1目标:x86_64-linux-gnu,标准模式,除了-Wall之外没有标志)是完全不同的:

#include <stdio.h>

struct lll {
    long l; 
    long long ll;
    };      

struct lld {
    long l; 
    long double ld;
    };      

struct lll lll1, lll2[2];
struct lld lld1, lld2[2];

int main(void)
{   
printf("lll1=%u, lll2=%u\n"
    , (unsigned) sizeof lll1
    , (unsigned) sizeof lll2
    );      

printf("lld=%u, lld2=%u\n"
    , (unsigned) sizeof lld1
    , (unsigned) sizeof lld2
    );      

return 0;
}

结果:

./a.out
lll1=16, lll2=32
lld=32, lld2=64

这可能是FUD(来自实际设法将未对齐的整数投入MBR的公司......)。但这也可能是由于作者没有得到太多的了解。

回答这个问题:施加对齐限制的硬件。编译器只需要实现它们。