获得对齐内存的最佳跨平台方法

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】:

只要你同意调用一个特殊的函数来进行释放,你的方法就可以了。我会反过来做你的#ifdefs:从标准指定的选项开始,然后回退到平台特定的选项。例如

    如果__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>

;

尚未测试此代码的跨平台性。

【讨论】:

您可以通过仅使用一个语句来简化deleteif(ptr) std::free(static_cast&lt;void**&gt;(ptr)[-1]); a static_assert(Alignment &gt; 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

【讨论】:

以上是关于获得对齐内存的最佳跨平台方法的主要内容,如果未能解决你的问题,请参考以下文章

C/C++获得对齐的内存的跨平台解决方案

SylixOS ARM平台下的内存对齐访问

内存对齐问题

内存对齐

让网站图标跨浏览器/平台工作的最佳方法

C语言重难点:内存对齐和位段