【问题标题】: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.htmlHow 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) 数据模型之间移植。

标签: c++ c performance sse memory-alignment


【解决方案1】:

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

  1. 如果__STDC_VERSION__ >= 201112L 使用aligned_alloc
  2. 如果_POSIX_VERSION >= 200112L 使用posix_memalign
  3. 如果定义了_MSC_VER,请使用Windows 的东西。
  4. ...
  5. 如果一切都失败了,只需使用 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 的对齐分配)。另见stackoverflow.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
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-08-24
      • 2010-10-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-01-20
      相关资源
      最近更新 更多