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

时间:2013-05-04 17:17:13

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

以下是我通常用于使用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)得到了我的aligned_malloc()函数的想法

编辑: 我刚刚发现MinGW未定义posix_memalign。但是,_mm_malloc适用于Visual Studio 2012,GCC,MinGW和Intel C ++编译器,因此它似乎是最方便的解决方案。它还需要使用自己的_mm_free函数,但在某些实现中,您可以将指针从_mm_malloc传递到标准free / delete

5 个答案:

答案 0 :(得分:10)

只要您可以通过调用特殊功能来执行释放,您的方法就可以了。我会以相反的方式做你的#ifdef:从标准指定的选项开始,然后回到平台特定的选项。例如

  1. 如果__STDC_VERSION__ >= 201112L使用aligned_alloc
  2. 如果_POSIX_VERSION >= 200112L使用posix_memalign
  3. 如果定义了_MSC_VER,请使用Windows内容。
  4. ...
  5. 如果所有其他方法都失败了,只需使用malloc / free并停用SSE / AVX代码。
  6. 如果您希望能够将分配的指针传递给free,问题就更难了;这在所有标准接口上都有效,但在Windows上没有,并且不一定与某些类似unix的系统具有遗留memalign功能。

答案 1 :(得分:4)

你提出的第一个功能确实可以正常工作。

你的“自制”功能也有效,但缺点是如果值已经对齐,你就浪费了15个字节。有时可能无关紧要,但OS可能能够提供正确分配而没有任何浪费的内存(如果需要将其对齐到256或4096字节,则可能会因添加“alignment-1”而浪费大量内存)字节)。

答案 2 :(得分:2)

这是一个固定的user2093113的示例,直接代码没有为我构建(void * unknown size)。我还把它放在模板类中覆盖operator new / delete,这样你就不必进行新的分配和调用。

#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>
{
};

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

答案 3 :(得分:1)

如果编译器支持它,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%便携式我不确定,但我希望有人会纠正我,如果没有!

答案 4 :(得分:0)

这是我的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
}