获得对齐内存的最佳跨平台方法
Posted
技术标签:
【中文标题】获得对齐内存的最佳跨平台方法【英文标题】:best cross-platform method to get aligned memory 【发布时间】:2013-04-28 22:06:52 【问题描述】:这是我通常用来与 Visual Studio 和 GCC 对齐内存的代码
inline void* aligned_malloc(size_t size, size_t align)
void *result;
#ifdef _MSC_VER
result = _aligned_malloc(size, align);
#else
if(posix_memalign(&result, align, size)) result = 0;
#endif
return result;
inline void aligned_free(void *ptr)
#ifdef _MSC_VER
_aligned_free(ptr);
#else
free(ptr);
#endif
这段代码一般都好吗?我还看到人们使用_mm_malloc
、_mm_free
。在大多数情况下,我想要对齐的内存是使用 SSE/AVX。我一般可以使用这些功能吗?这将使我的代码更简单。
最后,创建我自己的函数来对齐内存很容易(见下文)。那么为什么会有这么多不同的常用函数来获得对齐的内存(其中许多只在一个平台上工作)?
此代码进行 16 字节对齐。
float* array = (float*)malloc(SIZE*sizeof(float)+15);
// find the aligned position
// and use this pointer to read or write data into array
float* alignedArray = (float*)(((unsigned long)array + 15) & (~0x0F));
// dellocate memory original "array", NOT alignedArray
free(array);
array = alignedArray = 0;
请参阅:http://www.songho.ca/misc/alignment/dataalign.html 和 How to allocate aligned memory only using the standard library?
编辑: 万一有人关心,我从 Eigen (Eigen/src/Core/util/Memory.h) 中得到了我的对齐 malloc() 函数的想法
编辑:
我刚刚发现 posix_memalign
对于 MinGW 是未定义的。但是,_mm_malloc
适用于 Visual Studio 2012、GCC、MinGW 和 Intel C++ 编译器,因此它似乎是最方便的解决方案。它还需要使用自己的_mm_free
函数,尽管在某些实现中您可以将指针从_mm_malloc
传递到标准free
/ delete
。
【问题讨论】:
虽然unsigned long
地址转换可能在实践中有效,但它可能无法在 ILP32 / LP64 / LLP64 (win64) 数据模型之间移植。
【参考方案1】:
只要你同意调用一个特殊的函数来进行释放,你的方法就可以了。我会反过来做你的#ifdef
s:从标准指定的选项开始,然后回退到平台特定的选项。例如
-
如果
__STDC_VERSION__ >= 201112L
使用aligned_alloc
。
如果_POSIX_VERSION >= 200112L
使用posix_memalign
。
如果定义了_MSC_VER
,请使用Windows 的东西。
...
如果一切都失败了,只需使用 malloc
/free
并禁用 SSE/AVX 代码。
如果您希望能够将分配的指针传递给free
,问题就更难了;这在所有标准接口上都有效,但在 Windows 上无效,并且不一定适用于某些类 unix 系统具有的旧版 memalign
功能。
【讨论】:
【参考方案2】:您提出的第一个功能确实可以正常工作。
您的“自制”功能也可以使用,但缺点是如果值已经对齐,您就浪费了 15 个字节。有时可能无关紧要,但操作系统很可能能够提供正确分配的内存而不会造成任何浪费(如果它需要对齐到 256 或 4096 字节,您可能会通过添加“alignment-1”来浪费大量内存字节)。
【讨论】:
但是 _mm_malloc 呢?如果我使用 SSE/AVX,这通常可以使用吗?既然如此,为什么即使我没有使用 SSE/AVX 也不使用它? 如果编译器支持_mm_malloc(),那么这也是一个有效的解决方案。您还需要使用_mm_free()
。是的,当然,您可以将_mm_malloc()
用于任何内存分配——当然,小分配会比“通常”浪费更多的空间。
好的,我想我会开始使用 _mm_malloc() 和 _mm_free()。当我需要对齐的内存时,至少在 SO 答案上。它使代码更简单。【参考方案3】:
这是 user2093113 的固定示例,直接代码不是为我构建的(void* 未知大小)。我还将它放在一个覆盖 operator new/delete 的模板类中,这样您就不必进行分配和调用placement new。
#include <memory>
template<std::size_t Alignment>
class Aligned
public:
void* operator new(std::size_t size)
std::size_t space = size + (Alignment - 1);
void *ptr = malloc(space + sizeof(void*));
void *original_ptr = ptr;
char *ptr_bytes = static_cast<char*>(ptr);
ptr_bytes += sizeof(void*);
ptr = static_cast<void*>(ptr_bytes);
ptr = std::align(Alignment, size, ptr, space);
ptr_bytes = static_cast<char*>(ptr);
ptr_bytes -= sizeof(void*);
std::memcpy(ptr_bytes, &original_ptr, sizeof(void*));
return ptr;
void operator delete(void* ptr)
char *ptr_bytes = static_cast<char*>(ptr);
ptr_bytes -= sizeof(void*);
void *original_ptr;
std::memcpy(&original_ptr, ptr_bytes, sizeof(void*));
std::free(original_ptr);
;
像这样使用它:
class Camera : public Aligned<16>
;
尚未测试此代码的跨平台性。
【讨论】:
您可以通过仅使用一个语句来简化delete
:if(ptr) std::free(static_cast<void**>(ptr)[-1]);
a static_assert(Alignment > sizeof(void*))
是个好主意,或者在必要时使用min
增加space
。如果可用,您绝对还应该使用#ifdef
来使用对齐的分配器,而不是浪费空间。 (例如许多 C++ 编译器提供的 C11 的aligned_alloc)。另见***.com/questions/32612190/…【参考方案4】:
如果你的编译器支持它,C++11 会添加一个std::align
函数来进行运行时指针对齐。您可以像这样实现自己的 malloc/free(未经测试):
template<std::size_t Align>
void *aligned_malloc(std::size_t size)
std::size_t space = size + (Align - 1);
void *ptr = malloc(space + sizeof(void*));
void *original_ptr = ptr;
char *ptr_bytes = static_cast<char*>(ptr);
ptr_bytes += sizeof(void*);
ptr = static_cast<void*>(ptr_bytes);
ptr = std::align(Align, size, ptr, space);
ptr_bytes = static_cast<void*>(ptr);
ptr_bytes -= sizeof(void*);
std::memcpy(ptr_bytes, original_ptr, sizeof(void*));
return ptr;
void aligned_free(void* ptr)
void *ptr_bytes = static_cast<void*>(ptr);
ptr_bytes -= sizeof(void*);
void *original_ptr;
std::memcpy(&original_ptr, ptr_bytes, sizeof(void*));
std::free(original_ptr);
那么您不必保留原始指针值来释放它。这是否是 100% 便携我不确定,但如果不是,我希望有人能纠正我!
【讨论】:
在下面查看我的固定版本。修复了在 void* 上执行指针算术时的编译器错误,并且aligned_malloc 中的 memcpy 现在可以正确复制值。 这个版本总是浪费空间,即使对齐的分配器可用。 -1.【参考方案5】:这是我的 2 美分:
temp = new unsigned char*[num];
AlignedBuffers = new unsigned char*[num];
for (int i = 0; i<num; i++)
temp[i] = new unsigned char[bufferSize +15];
AlignedBuffers[i] = reinterpret_cast<unsigned char*>((reinterpret_cast<size_t>
(temp[i% num]) + 15) & ~15);// 16 bit alignment in preperation for SSE
【讨论】:
以上是关于获得对齐内存的最佳跨平台方法的主要内容,如果未能解决你的问题,请参考以下文章