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
是一个完整的对象类型,具有最严格的基本对齐方式。
请注意,char
或 unsigned 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 的可移植替代品? 根据标准malloc
和new
应该可以工作。 _aligned_malloc
是因为 MS/Intel 引入了过度对齐的类型并遇到了问题。而不是修复malloc
,并冒着破坏旧代码的风险,我们得到了_aligned_malloc
。
malloc 不支持 在任意大小上对齐,这可能是 user882903 想要的。【参考方案3】:
看看std::aligned_storage
和alignas()
运算符。它们是 C++11 的一部分,似乎正是您正在寻找的。p>
【讨论】:
30.9.7.6p2:“是否支持任何扩展对齐由实现定义”参考std::aligned_storage
。所以这也不是便携式的。
Stativ_Assert 所需的对齐可用。【参考方案4】:
C++03 和 C++0x 有operator new
。
new T
或 new 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 中的动态对齐内存分配的主要内容,如果未能解决你的问题,请参考以下文章