SSEを使用してデータを処理する場合は、std::vector16バイトのアライメントが必要です。どうすればそれを達成できますか?自分でアロケータを書く必要がありますか?または、デフォルトのアロケータはすでに16バイト境界に整列していますか?
8 に答える
C++ 標準では、任意の標準型に適切にアラインされたメモリを割り当てるために、割り当て関数 (malloc()および) が必要です。これらの関数はアラインメント要件を引数として受け取らないため、実際には、すべての割り当てのアラインメントが同じであり、最大のアラインメント要件を持つ標準型のアラインメントであることを意味します。多くの場合、and/or ( boostを参照) max_align ユニオン)。operator new()long doublelong long
SSE や AVX などのベクトル命令には、標準の C++ 割り当て関数で提供されるものよりも強力なアライメント要件があります (128 ビット アクセスでは 16 バイトでアライメントされ、256 ビット アクセスでは 32 バイトでアライメントされます)。posix_memalign()またはmemalign()、より強力なアライメント要件でそのような割り当てを満たすために使用できます。
C++17 では、割り当て関数は type の追加の引数を受け入れstd::align_val_tます。
次のように使用できます。
#include <immintrin.h>
#include <memory>
#include <new>
int main() {
std::unique_ptr<__m256i[]> arr{new(std::align_val_t{alignof(__m256i)}) __m256i[32]};
}
さらに、C++17 では、標準のアロケーターが型のアラインメントを尊重するように更新されているため、次のように簡単に実行できます。
#include <immintrin.h>
#include <vector>
int main() {
std::vector<__m256i> arr2(32);
}
または (C++11 ではヒープ割り当てが含まれておらず、サポートされていません):
#include <immintrin.h>
#include <array>
int main() {
std::array<__m256i, 32> arr3;
}
std::などのコンテナーでカスタム アロケーターを使用する必要がありますvector。次のものを誰が書いたか思い出せませんが、私はしばらくの間それを使用しましたが、うまくいくようです (コンパイラ/プラットフォームによっては、に変更_aligned_mallocする必要があるかもしれません):_mm_malloc
#ifndef ALIGNMENT_ALLOCATOR_H
#define ALIGNMENT_ALLOCATOR_H
#include <stdlib.h>
#include <malloc.h>
template <typename T, std::size_t N = 16>
class AlignmentAllocator {
public:
typedef T value_type;
typedef std::size_t size_type;
typedef std::ptrdiff_t difference_type;
typedef T * pointer;
typedef const T * const_pointer;
typedef T & reference;
typedef const T & const_reference;
public:
inline AlignmentAllocator () throw () { }
template <typename T2>
inline AlignmentAllocator (const AlignmentAllocator<T2, N> &) throw () { }
inline ~AlignmentAllocator () throw () { }
inline pointer adress (reference r) {
return &r;
}
inline const_pointer adress (const_reference r) const {
return &r;
}
inline pointer allocate (size_type n) {
return (pointer)_aligned_malloc(n*sizeof(value_type), N);
}
inline void deallocate (pointer p, size_type) {
_aligned_free (p);
}
inline void construct (pointer p, const value_type & wert) {
new (p) value_type (wert);
}
inline void destroy (pointer p) {
p->~value_type ();
}
inline size_type max_size () const throw () {
return size_type (-1) / sizeof (value_type);
}
template <typename T2>
struct rebind {
typedef AlignmentAllocator<T2, N> other;
};
bool operator!=(const AlignmentAllocator<T,N>& other) const {
return !(*this == other);
}
// Returns true if and only if storage allocated from *this
// can be deallocated from other, and vice versa.
// Always returns true for stateless allocators.
bool operator==(const AlignmentAllocator<T,N>& other) const {
return true;
}
};
#endif
次のように使用します (必要に応じて、16 を別の配置に変更します)。
std::vector<T, AlignmentAllocator<T, 16> > bla;
ただし、これは、使用するメモリ ブロックstd::vectorが 16 バイトにアラインされていることを確認するだけです。が 16 の倍数でない場合sizeof(T)、要素の一部が整列されません。データ型によっては、これは問題にならない場合があります。(4 バイト) の場合、インデックスが 4 の倍数である要素のみを読み込みます。( T8バイト) の場合、2 の倍数のみを読み込みます。intdouble
本当の問題は、クラスを as として使用するT場合です。その場合、クラス自体でアライメント要件を指定する必要があります (コンパイラによっては、これが異なる場合があります。例は GCC の場合です)。
class __attribute__ ((aligned (16))) Foo {
__attribute__ ((aligned (16))) double u[2];
};
もうすぐ完成です!Visual C++ (少なくともバージョン 2010) を使用している場合std::vector、std::vector::resize.
コンパイル時に、次のエラーが発生した場合:
C:\Program Files\Microsoft Visual Studio 10.0\VC\include\vector(870):
error C2719: '_Val': formal parameter with __declspec(align('16')) won't be aligned
stl::vector headerファイルをハックする必要があります。
- ヘッダー ファイルを見つけ
vectorます [C:\Program Files\Microsoft Visual Studio 10.0\VC\include\vector] - メソッドを見つけます
void resize( _Ty _Val )[VC2010 の 870 行目] - に変更し
void resize( const _Ty& _Val )ます。
簡潔な答え:
もしそうならsizeof(T)*vector.size() > 16、はい。
ベクターが通常のアロケーターを使用すると仮定します
alignof(std::max_align_t) >= 16警告: これが最大アライメントである限り。
長い答え:
2017 年 8 月 25 日更新 新規格n4659
16 を超えるものに対して整列されている場合は、16 に対しても正しく整列されます。
6.11 アラインメント(パラグラフ 4/5)
アライメントは std::size_t 型の値として表されます。有効なアラインメントには、基本型の alignof 式によって返される値と、空の場合がある追加の実装定義の値セットのみが含まれます。すべてのアラインメント値は、負でない 2 の整数乗でなければなりません。
アライメントには、弱いアライメントから強いアライメント、または厳密なアライメントへの順序があります。アラインメントが厳密なほど、アラインメントの値が大きくなります。アラインメント要件を満たすアドレスは、より弱い有効なアラインメント要件も満たします。
new および new[] は、オブジェクトがサイズに対して正しく整列されるように整列された値を返します。
8.3.4 新規 (第 17 項)
[ 注: 割り当て関数が null 以外の値を返す場合、それは、オブジェクト用のスペースが予約されているストレージのブロックへのポインターでなければなりません。ストレージのブロックは、適切に位置合わせされ、要求されたサイズであると想定されます。オブジェクトが配列の場合、作成されたオブジェクトのアドレスはブロックのアドレスと必ずしも同じではありません。— エンドノート]
ほとんどのシステムには最大アライメントがあることに注意してください。動的に割り当てられたメモリは、これよりも大きな値に調整する必要はありません。
6.11 アラインメント(パラグラフ 2)
基本的なアラインメントは、すべてのコンテキストで実装によってサポートされる最大のアラインメント以下のアラインメントによって表されます。これは、alignof(std::max_align_t) (21.2) と同じです。型に必要なアラインメントは、完全なオブジェクトの型として使用される場合と、サブオブジェクトの型として使用される場合とで異なる場合があります。
したがって、割り当てられたベクトル メモリが 16 バイトを超える限り、16 バイト境界に正しく配置されます。
独自のアロケーターを作成します。allocateそしてdeallocate重要なものです。以下に一例を示します。
pointer allocate( size_type size, const void * pBuff = 0 )
{
char * p;
int difference;
if( size > ( INT_MAX - 16 ) )
return NULL;
p = (char*)malloc( size + 16 );
if( !p )
return NULL;
difference = ( (-(int)p - 1 ) & 15 ) + 1;
p += difference;
p[ -1 ] = (char)difference;
return (T*)p;
}
void deallocate( pointer p, size_type num )
{
char * pBuffer = (char*)p;
free( (void*)(((char*)p) - pBuffer[ -1 ] ) );
}
declspec(align(x,y))Intel のベクトル化チュートリアルで説明されているように使用します。 http://d3f8ykwhia686p.cloudfront.net/1live/intel/CompilerAutovectorizationGuide.pdf
STLコンテナについては何も想定しないでください。それらのインターフェース/動作は定義されていますが、その背後にあるものは定義されていません。生のアクセスが必要な場合は、必要なルールに従う独自の実装を作成する必要があります。
標準では、SSEを含む任意のnewデータ型に合わせてデータをnew[]返すことが義務付けられています。MSVCが実際にそのルールに従うかどうかは別の問題です。