malloc()和free()的对齐限制

9

我曾阅读过较旧的 K&R(第2版)和其他涉及以 malloc()free() 的风格实现动态内存分配器的 C 语言文本,通常也会顺便提到与数据类型对齐限制有关的内容。显然,某些计算机硬件架构(CPU、寄存器和存储器访问)会限制如何存储和寻址某些值类型。例如,4 字节(long)整数必须从地址的倍数开始存储可能是一种要求。

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

5个回答

6
Sparc、MIPS、Alpha和大多数其他“经典RISC”架构仅允许对内存进行对齐访问,即使在今天也是如此。未对齐的访问将导致异常,但一些操作系统将通过使用较小的负载和存储从所需地址在软件中复制来处理异常。应用程序代码不会知道存在问题,除了性能非常差之外。
MIPS具有特殊指令(lwl和lwr),可用于从不对齐的地址访问32位数量。每当编译器可以确定地址可能未对齐时,它将使用这两个指令序列而不是普通的lw指令。
x86可以在硬件中处理不对齐的内存访问而不会引发异常,但与对齐访问相比,仍会有高达3倍的性能损失。
Ulrich Drepper撰写了一篇关于此及其他与内存相关的主题的综合论文,What Every Programmer Should Know About Memory。它是一篇非常长的论文,但充满了有趣的内容。

4

对于IT技术而言,对齐问题至关重要。一些处理器(例如68k系列)会在尝试访问奇数边界上的字值时抛出异常。如今,大多数处理器将运行两个内存周期以获取非对齐字,但这肯定比对齐获取慢得多。其他一些处理器甚至不会抛出异常,但却会从内存中获取错误的值!

除了性能之外,遵循处理器的对齐偏好是明智之举。通常,编译器会处理所有细节,但如果您自己布置内存结构,则值得考虑。


1

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

例如:

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

假设在x86上运行Windows,则大小为4 * 4 = 16字节。

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

这个变量的大小将会是4*3=12字节。

这是因为编译器对于整数强制使用4字节对齐,但对于字符只使用1字节对齐。

通常情况下,将相同大小(类型)的成员变量打包在一起,以最小化浪费空间。


1

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

您可能会看到pragma指令(在C/C++中),用于更改对齐方式。只有在需要非常特定的对齐方式时才应使用它。

// For example, this changes the pack to 2 byte alignment.
#pragma pack(2)

1
请注意,即使在IA-32和AMD64上,一些SSE指令/内嵌函数也需要对齐的数据。如果数据未对齐,这些指令将抛出异常,因此您至少不必调试“错误数据”错误。同样,也有等效的非对齐指令,但像Denton所说,它们速度较慢。
如果您使用VC ++,除了#pragma pack指令之外,还可以使用__declspec(align)指令进行精确对齐。VC++文档还提到了一个__aligned_malloc函数,用于特定的对齐要求。
作为经验法则,除非您正在跨编译器/语言移动数据或正在使用SSE指令,否则您可能可以忽略对齐问题。

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