什么是对齐内存分配?

Posted

技术标签:

【中文标题】什么是对齐内存分配?【英文标题】:What is aligned memory allocation? 【发布时间】:2010-10-22 05:05:10 【问题描述】:

我也想知道 glibc malloc() 是否这样做。

【问题讨论】:

那不是依赖于目标架构吗?它几乎肯定是对齐的...... 【参考方案1】:

假设你有这个结构。

struct S 
    short a;
    int b;
    char c, d;
;

如果没有对齐,它将像这样在内存中布局(假设是 32 位架构):

 0 1 2 3 4 5 6 7
|a|a|b|b|b|b|c|d|  bytes
|       |       |  words

问题在于,在某些 CPU 架构上,从内存中加载 4 字节整数的指令仅适用于字边界。因此,您的程序必须使用单独的指令获取 b 的每一半。

但如果内存布局为:

 0 1 2 3 4 5 6 7 8 9 A B
|a|a| | |b|b|b|b|c|d| | |
|       |       |       |

然后访问b 变得简单。 (缺点是需要更多内存,因为填充字节。)

不同的数据类型有不同的对齐要求。 char 是 1 字节对齐的,short 是 2 字节对齐的,而 4 字节类型(intfloat 和 32 位系统上的指针)通常是 4 字节对齐。

C 标准要求malloc 返回一个针对任何数据类型正确对齐的指针。

x86-64 上的 glibc malloc 返回 16 字节对齐的指针。

【讨论】:

抱歉,我不明白您所说的“char 通常是 1 字节对齐、short 是 2 字节对齐和 4 字节类型”是什么意思。 @Anni_housie 这只是意味着,大多数系统通常使用 1byte 的内存来存储 char,2bytes 用于存储 short,4bytes 用于 int/float/pointer 等等 或者您可以重新排序结构中的元素,使其首先具有 int,然后是 short,然后是两个字符。这样系统就可以轻松读取它们。 确实,由于成员的对齐方式,结构成员的列出顺序会影响结构的最终大小。 @dan04 感谢您的回答,现在我可以明白为什么在 a 和 b 之间插入 2 个字节的填充。我在某处读到,在您的示例中,在成员 c 之后的末尾还会插入另外 2 个字节的填充以对齐整个结构,为什么需要这样做?【参考方案2】:

Alignment 要求指定哪些地址偏移可以分配给哪些类型。这完全取决于实现,但通常基于字长。例如,一些 32 位架构要求所有 int 变量以 4 的倍数开始。在某些架构上,对齐要求是绝对的。在其他(例如 x86)上,无视它们只会带来性能损失。

malloc 需要返回适合任何对齐要求的地址。换句话说,返回的地址可以分配给任何类型的指针。来自 C99 §7.20.3(内存管理功能):

分配时返回的指针 成功被适当地对齐,以便 它可以分配给指向任何 对象类型,然后用于访问 这样的对象或这样的数组 分配空间中的对象(直到 该空间被显式释放)。

【讨论】:

您提供的链接不好。你能再提供一个吗?那个有多个问题!泰【参考方案3】:

如果您有特定的内存对齐需求(针对特定硬件或库),您可以查看不可移植的内存分配器,例如 _aligned_malloc()memalign()。这些可以很容易地在“可移植”接口后面抽象出来,但不幸的是它们是非标准的。

【讨论】:

【参考方案4】:

malloc() 文档说:

[...] the allocated memory that is suitably aligned for any kind of variable.

这对于您在 C/C++ 中所做的大多数事情都是正确的。但是,正如其他人指出的那样,存在许多特殊情况并需要特定的对齐方式。例如,Intel 处理器支持 256 位类型:__m256malloc() 肯定不会考虑到这一点。

同样,如果您想为要分页的数据分配内存缓冲区(类似于mmap() 返回的地址等),那么您可能需要一个非常大的对齐方式,如果@ 987654326@ 是返回始终与此类边界对齐的缓冲区。

在Linux或其他Unix系统下,建议你使用posix_memalign()函数:

int posix_memalign(void **memptr, size_t alignment, size_t size);

这是满足此类需求的最新功能。

【讨论】:

以上是关于什么是对齐内存分配?的主要内容,如果未能解决你的问题,请参考以下文章

为啥动态分配的内存总是 16 字节对齐?

分配粒度和内存页面大小(x86处理器平台的分配粒度是64K,内存页是4K,所以section都是0x1000对齐,硬盘扇区大小是512字节,所以PE文件默认文件对齐是0x200)

C++11 中的动态对齐内存分配

为 SIMD 分配内存对齐的缓冲区; |16 如何给出 16 的奇数倍数,为啥要这样做?

malloc分配内存进行对齐的操作

c语言数组在内存中是怎么分配的?