SSE 等の SIMD 命令やキャッシュを意識したプログラムではメモリのアライメントを
考慮する必要があります。
これまではコンパイラ毎の拡張命令に頼っていましたが、
C++11 では言語仕様に含まれるようになりました。
メモリアクセスの同期命令も含めて、低レベルなメモリ命令を積極的に取り込んでいる印象です。
CC alignas alignof ----------------------------------------------------------------- VisualC++ __declspec(align(byte)) __alignof(type) gcc/clang __attribute__((aligned(byte)) __alignof__(type) C++11 alignas(byte) alignof(type)
alignas はメモリ配置時のアライメントを宣言します。
// 変数宣言 __declspec(align(32)) int array[8]; // VC int array[8] __attribute((aligned(32)); // gcc/clang alignas(32) int array[8]; // C++11
↑変数 array は 32byte 単位のアドレス境界に配置されます。
// 型宣言 // VC __declspec(align(32)) struct AVECT { float pos[4]; }; struct __declspec(align(32)) AVECT { float pos[4]; }; // gcc/clang struct AVECT { float pos[4]; } __attribute((aligned(32)); struct __attribute((aligned(32)) AVECT { float pos[4]; }; // C++11 struct alignas(32) AVECT { float pos[4]; }; // 使用時 AVECT position;
↑ position も 32byte 単位のアドレスに配置します。
VC と gcc/clang の attribute は若干書式に違いがあります。
alignas を VC のように struct の前に記述すると変数への修飾となり、
型宣言に含まれません。VC では含まれているようです↓。
alignas(32) struct AVECT2 { float pos[4]; } va; // VC Nov 2013 CTP : alignof(AVECT2) == 32 // gcc4.8/clang3.4 : alignof(AVECT2) == 4
static (global) に宣言した変数は静的にアドレスが求まるので、
コンパイル時 (link時) に解決します。
ただしプログラム (data segment) がロードされるアドレスが、
より大きなアドレス境界に配置していることが条件となります。
(1) 基準となるメモリアドレスのアライメント
(2) メモリを pack したときの整合性
つまりコンパイラ側が行うのは (2) と (1) の一部になります。
基準アドレス(1)の生成 ------------------------------------------------ data OS が行う stack 実行時に行う (コンパイラが自動的に生成) heap ライブラリまたはユーザー任せ
stack に宣言した変数は、実行するまでアドレスがわからないので
実行時に端数を切り捨てるコードが埋め込まれます。
; clang x64 alignas(32) pushq %rbps andq $-32, %rsp ; alignas(32) subq $32, %rsp ; int array[8]
; clang armv7l alignas(32) add r11, sp, #8 sub sp, sp, #80 bic sp, sp, #31 ; alignas(32)
heap の場合は確保したメモリが alignment に従っていない可能性があるため
利用時にアドレス整合を意識しなければなりません。
alignof を用いるとコンパイル時に型情報を判断できるので、alignment に
対応したアロケータを作ることができます。
templateinline T* flNew( A0&&... a0 ) { return new( malloc_aligned( sizeof(T), alignof(T) ) ) T( std::forward (a0)... ); } template inline void flDelete( T* ptr ) { ptr->~T(); free_aligned( ptr ); }
この flNew() で作成した AVECT ↓は、宣言時に alignas(32) が指定されているため
32byte 単位で確保されます。(malloc_aligned が実装されていると仮定する)
AVECT* ap= flNew(); // alignas(32)
関連エントリ
・VisualStudio と C++11 、コンパイラの違いなど
・C++11 Lambda function ラムダ関数
・C++11 Variadic Templates 可変長引数のテンプレート
・スレッド同期命令の比較 C++11 とコンパイラ
・C++11 Rvalue Reference 右辺値参照