web-dev-qa-db-ja.com

整列されたメモリを取得するための最良のクロスプラットフォーム方式

これは、VisualStudioと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を使用します。これらの機能を一般的に使用できますか?それは私のコードをずっと簡単にするでしょう。

最後に、メモリを整列させる独自の関数を簡単に作成できます(以下を参照)。では、なぜ、アラインされたメモリを取得するための非常に多くの異なる共通関数があるのでしょうか(その多くは1つのプラットフォームでのみ機能します)。

このコードは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に対して未定義であることを発見しました。ただし、_mm_mallocはVisualStudio 2012、GCC、MinGW、およびIntel C++コンパイラで機能するため、一般的に最も便利なソリューションのようです。また、独自の_mm_free関数を使用する必要がありますが、一部の実装では、_mm_mallocから標準のfree/deleteにポインターを渡すことができます。

28
user2088790

あなたが提案する最初の関数は確かにうまくいくでしょう。

「自作」関数も機能しますが、値がすでに整列されている場合、15バイトを無駄にしているという欠点があります。時々問題にならないかもしれませんが、OSは無駄なく正しく割り当てられたメモリを提供できる可能性があります(256バイトまたは4096バイトに整列する必要がある場合は、「alignment-1」を追加することで大量のメモリを浪費するリスクがありますバイト)。

4
Mats Petersson

解放を行うために特別な関数を呼び出さなければならないことに問題がない限り、アプローチは問題ありません。ただし、#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関数では必ずしも有効ではありません。

10
R..

これはuser2093113のサンプルの修正であり、直接コードは私のためにビルドされませんでした(void *不明なサイズ)。また、演算子new/deleteをオーバーライドするテンプレートクラスに配置したので、割り当てを行ったり、placementnewを呼び出す必要はありません。

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

このコードのクロスプラットフォーム性はまだテストしていません。

2
speps

コンパイラがそれをサポートしている場合、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%ポータブルかどうかはわかりませんが、そうでない場合は誰かが私を修正してくれることを願っています!

1
user2093113

これが私の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
}
0
Mikhail