9

我读过的较早的 K&R(第 2 版)和其他 C 语言文本讨论了动态内存分配器的实现方式,malloc()并且free()通常还顺便提到了一些关于数据类型对齐限制的事情。显然,某些计算机硬件架构(CPU、寄存器和内存访问)限制了您如何存储和处理某些值类型。例如,可能要求 4 字节 ( long) 整数必须从四的倍数地址开始存储。

主要平台(英特尔和 AMD、SPARC、Alpha)对内存分配和内存访问施加了哪些限制(如果有),或者我可以安全地忽略在特定地址边界上对齐内存分配吗?

4

5 回答 5

6

Sparc、MIPS、Alpha 和大多数其他“经典 RISC”架构仅允许对齐访问内存,即使在今天也是如此。未对齐的访问将导致异常,但某些操作系统将通过使用较小的加载和存储从软件中的所需地址进行复制来处理异常。应用程序代码不会知道有问题,只是性能会很差。

MIPS 具有特殊指令(lwl 和 lwr),可用于从未对齐的地址访问 32 位数量。每当编译器可以判断地址可能未对齐时,它将使用这两个指令序列而不是普通的 lw 指令。

x86 可以无一例外地处理硬件中未对齐的内存访问,但与对齐访问相比,性能仍然高达 3 倍。

Ulrich Drepper 写了一篇关于这个和其他与内存相关的主题的综合论文,每个程序员应该知道的关于内存的知识。这是一篇很长的文章,但充满了耐嚼的善良。

于 2008-09-06T05:57:36.547 回答
4

今天,对齐仍然非常重要。如果您尝试访问奇数边界上的字值,某些处理器(想到 68k 系列)会抛出异常。今天,大多数处理器将运行两个内存周期来获取未对齐的字,但这肯定会比对齐的获取要慢。其他一些处理器甚至不会抛出异常,而是会从内存中获取不正确的值!

如果仅出于性能原因,尝试遵循处理器的对齐偏好是明智的。通常,您的编译器会处理所有细节,但如果您正在自己布置内存结构的任何事情,那么值得考虑。

于 2008-08-30T23:11:51.990 回答
1

在 C(++) 中布局类或结构时,您仍然需要注意对齐问题。在这些情况下,编译器会为您做正确的事情,但结构/类的整体大小可能比必要的更浪费

例如:

struct
{ 
    char A;
    int B;
    char C;
    int D;
};

大小为 4 * 4 = 16 字节(假设 x86 上的 Windows)而

struct
{ 
    char A;
    char C;
    int B;
    int D;
};

大小为 4*3 = 12 个字节。

这是因为编译器对整数强制执行 4 字节对齐,但对字符强制执行 1 字节对齐。

通常将相同大小(类型)的成员变量打包在一起以最大程度地减少浪费的空间。

于 2008-08-30T23:22:35.707 回答
1

正如 Greg 提到的,它在今天仍然很重要(在某些方面可能更重要),编译器通常会根据架构的目标来处理对齐。在托管环境中,JIT 编译器可以基于运行时架构优化对齐。

您可能会看到更改对齐方式的编译指示指令(在 C/C++ 中)。这仅应在需要非常具体的对齐时使用。

// For example, this changes the pack to 2 byte alignment.
#pragma pack(2)
于 2008-08-31T01:32:57.063 回答
1

请注意,即使在 IA-32 和 AMD64 上,一些 SSE 指令/内在函数也需要对齐的数据。如果数据未对齐,这些指令将引发异常,因此至少您不必调试“错误数据”错误。也有等效的未对齐指令,但就像 Denton 所说,它们速度较慢。

如果您使用的是 VC++,那么除了 #pragma pack 指令外,您还可以使用 __declspec(align) 指令进行精确对齐。VC++ 文档还提到了针对特定对齐要求的 __aligned_malloc 函数。

根据经验,除非您跨编译器/语言移动数据或使用 SSE 指令,否则您可能会忽略对齐问题。

于 2008-09-06T06:19:35.777 回答