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

32

这是我通常使用的用于在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如何仅使用标准库分配对齐内存?

编辑: 如果有人在意的话,我从Eigen (Eigen/src/Core/util/Memory.h)获取了我的aligned_malloc()函数的想法。

编辑: 我刚刚发现,posix_memalign在MinGW中未定义。然而,在Visual Studio 2012、GCC、MinGW和Intel C++编译器中,_mm_malloc都可以使用,因此它似乎是一般情况下最方便的解决方案。它还需要使用自己的_mm_free函数,不过在某些实现中,您可以将来自_mm_malloc的指针传递给标准的free / delete


尽管将地址强制转换为“unsigned long”在实践中可能有效,但在ILP32 / LP64 / LLP64(win64)数据模型之间可能不可移植。 - Brett Hale
5个回答

14
只要您不介意需要调用特殊函数来完成释放,那么您的方法是可以的。但我会将您的#ifdef条件判断反过来:首先使用标准指定的选项,然后回退到特定于平台的选项。例如:
  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函数上可能无效。

5

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

你的“自制”函数也可以工作,但缺点是如果值已经对齐,则刚刚浪费了15个字节。有时可能无关紧要,但操作系统很可能能够提供正确分配内存而没有任何浪费(如果需要将其对齐到256或4096字节,则通过添加“对齐-1”字节会浪费大量内存)。


但是_mm_malloc呢?如果我使用SSE/AVX,通常使用它是否可以?说到这个,既然如此,为什么不在我不使用SSE/AVX时也使用它呢? - user2088790
2
如果编译器支持_mm_malloc(),那么这也是一个有效的解决方案。您还需要使用_mm_free()。当然,您可以使用_mm_malloc()进行任何内存分配,但是小的分配将比“通常”浪费更多空间。 - Mats Petersson
好的,我想我会开始使用_mm_malloc()和_mm_free()。至少在SO答案中当我需要对齐内存时。这使得代码更简单。 - user2088790

2
这是user2093113的一个示例,但直接的代码对我无法构建(void* unknown size)。我还将其放在一个模板类中,重载了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>
{
};

我还没有测试这段代码的跨平台性。


1
你可以通过使用一条语句来简化delete操作:if(ptr) std::free(static_cast<void**>(ptr)[-1]); - bit2shift
一个 static_assert(Alignment > sizeof(void*)) 是个好主意,或者如果需要的话使用 min 来增加 space。你应该一定要使用 #ifdef 来使用可用的对齐分配器,而不是浪费空间。(例如 C11 的 aligned_alloc,许多 C++ 编译器都提供)。另请参见 https://dev59.com/n1wY5IYBdhLWcg3wcXeE - Peter Cordes

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%可移植,但如果不是,我希望有人能纠正我!


请看我下面的修复版本。修复了在void*指针上进行指针算术运算的编译器错误,并且现在aligned_malloc中的memcpy正确地复制了值。 - speps
这个版本即使有对齐分配器也总是浪费空间。-1。 - Peter Cordes

0

这是我的两分钱意见:

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
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接