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

Posted

技术标签:

【中文标题】C++11 中的动态对齐内存分配【英文标题】:Dynamic aligned memory allocation in C++11 【发布时间】:2011-08-07 16:18:48 【问题描述】:

Windows 上的posix_memalign_aligned_malloc 允许动态分配对齐的内存块。 C ++ 11中有类似的东西吗?据我所知,alignas 关键字仅适用于静态分配的对象。

【问题讨论】:

对齐是指顺序吗?如果是,则使用new 运算符和[] 组合分配的内存是动态对齐的。 @Blindy:我不会说“相反”。更像“正交”? 对齐是指地址模指定值为零。例如调用 posix_memalign(&ptr, 32, 1000) 保证在 ptr 中返回的地址可以被 32 整除。我需要它来在缓存行边界分配对象以避免错误共享。 恐怕没有标准的 C++ 分配器支持任意对齐。您必须使用特定于平台的分配 API。但是,如果您将它与我给您的构造函数/析构函数的任意内存示例结合使用,这应该不是什么大问题。 C++17 将添加为动态分配的对象指定“自定义”/过度对齐的功能:open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3396.htm 最佳答案中提到的这个提议已被即将发布的标准接受。跨度> 【参考方案1】:

这取决于您需要什么对齐方式。对于 alignof(std::max_align_t) 的任何内容,new 按照 n3242 3.7.4.1/2 工作:

返回的指针应适当对齐,以便它可以 转换为具有基本的任何完整对象类型的指针 对齐要求

std::max_align_t 是一个完整的对象类型,具有最严格的基本对齐方式。

请注意,charunsigned char 而不是 signed char 的数组分配在 5.3.4/10 中有不同的规则:

对于 char 和 unsigned char 的数组,结果之间的差异 新表达式和分配返回的地址 函数应是最严格基本函数的整数倍 大小为 no 的任何对象类型的对齐要求 (3.11) 大于正在创建的数组的大小。

所以new char[1]; 的对齐方式可以是 1。

至于分配对齐大于alignof(std::max_align_t) 的内存,C++11 没有提供直接的方法来做到这一点。唯一可靠的方法是至少分配size + alignment 字节并使用std::align 在此缓冲区中获得正确对齐的位置。

这会浪费大量内存,因此如果您需要大量内存,您可以创建一个分配器,为所有内存分配足够大的块并在其上使用 std::align。然后,您的开销将在所有分配中摊销。

您的另一个选择是等待http://open-std.org/JTC1/SC22/WG21/docs/papers/2012/n3396.htm 使其成为标准。

就我个人而言,我只会在操作系统提供的 API 上编写一个抽象层来分配对齐的内存。

【讨论】:

【参考方案2】:

您可以使用 posix_memalign/_aligned_malloc 分配一块内存,然后使用特殊的“new”运算符语法来初始化该内存区域中的对象。像这样的:

// Allocate raw memory for a Foo object.
void *mem;
size_t alignment = 0x1000;
size_t size = ?;
posix_memalign(&mem, alignment, size);
// Call the constructor on the allocated memory.
Foo *foo = new (mem) Foo(...);

// Now you have a useable object.
foo->some_method();

// Call destructor without freeing object memory.
foo->~Foo();
// Free raw memory.
free(foo);

【讨论】:

谢谢,对。我想知道的是 C++11 中是否有 posix_memalign 和 _aligned_malloc 的可移植替代品? 根据标准mallocnew 应该可以工作。 _aligned_malloc 是因为 MS/Intel 引入了过度对齐的类型并遇到了问题。而不是修复malloc,并冒着破坏旧代码的风险,我们得到了_aligned_malloc malloc 不支持 在任意大小上对齐,这可能是 user882903 想要的。【参考方案3】:

看看std::aligned_storagealignas() 运算符。它们是 C++11 的一部分,似乎正是您正在寻找的。​​p>

【讨论】:

30.9.7.6p2:“是否支持任何扩展对齐由实现定义”参考std::aligned_storage。所以这也不是便携式的。 Stativ_Assert 所需的对齐可用。【参考方案4】:

C++03 和 C++0x 有operator new

new Tnew T[] 保证为 T 类型的对象返回正确对齐的内存。

new char[]new signed char[]new unsigned char[] 保证为 any 对象返回正确对齐的内存,以便您可以在其上使用新位置。

【讨论】:

这些不适用于“任何”对象。例如,一个必须在页面边界分配的对象:当然没有 C++ 实现将所有分配对齐到 4096 字节边界。它保证支持任何标准的 C++ 原始类型(双精度通常是最严格的类型),但 PC 上的大多数编译器/运行时不支持对齐 SSE 类型的对齐要求(例如 16 字节对齐)。运算符 new 不对其所使用的类型进行对齐(或者实际上是任何元数据)这一事实是该语言的一个巨大缺陷。 标准实际上所说的是“任何具有基本对齐要求的对象类型”,它肯定不符合 ANY 对象。 @SeanMiddleditch:实际上,它至少是任何内置类型。对于支持的最小对齐方式,请检查std::max_align_t 类型,因为std::malloc 保证返回至少与该对齐方式对齐的内存。实现可以随意返回更高的对齐方式。【参考方案5】:

对于在堆上分配的对齐内存,我使用来自http://code.google.com/p/c-plus/source/browse/src/util.h#57 的 align() 实现,因为我的 gcc4.8 似乎不支持它。这是一个示例代码:

typedef float TItem;
static const int SIZE = 100;
static const int ALIGNMENT = 16;

// allocate heap storage larger then SIZE
TItem* storage = new TItem[SIZE + (ALIGNMENT / sizeof(TItem))];
void* storage_ptr = (void*)storage;
size_t storage_size = sizeof(TItem) * (SIZE + 1);
// aligned_array should be properly aligned
TItem* aligned_array = (TItem*) align(MEM_ALIGNMENT, sizeof(TItem) * SIZE, storage_ptr, storage_size);
if (!aligned_array)  throw std::bad_alloc(); 

【讨论】:

【参考方案6】:

英特尔的TBB 提供了一个便携式cache_aligned_allocator,我认为您可能正在寻找它。

【讨论】:

【参考方案7】:

C++ 标准始终保证从堆分配中任何对象的适当对齐 - 即,

template<typename T> T* func() 
    char* buf = new char[sizeof(T)];
    return new(buf) T();

保证不会因为对齐原因而失败。

【讨论】:

大声笑,我们在同一秒回答了完全相同的问题 :) 对不起,我认为我的问题不够精确。我想像 posix_memalign 一样明确指定边界。问的原因是我想在不同的缓存行分配小对象以避免错误共享。 我不相信你能做到。您可以手动填充大小并分配更大的结构。 这个答案是错误的,分配函数只支持对齐要求不比alignof(std::max_align_t)更严格的对象。请参见 3.7.4.1p2(或 5.3.4p10)和 3.11p2。也就是说,对象必须具有“基本对齐”。

以上是关于C++11 中的动态对齐内存分配的主要内容,如果未能解决你的问题,请参考以下文章

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

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

c中的核心转储和动态内存分配

如何使用 malloc 为 C 中的结构创建动态内存分配?

C语言中的动态内存分配的用法举例

自定义堆栈分配器中的 C++ 内存对齐