align (C++)
Microsoft 固有の仕様 →
ユーザー定義データ (関数の静的割り当てや自動データなど) のアラインメントを正確に制御するには、__declspec(align(#)) を使用します。
__declspec( align( # ) ) declarator
解説
最新のプロセッサ命令を使用するアプリケーションの記述では、いくつかの新しい制約や問題があります。 特に、多くの新しい命令では、データを 16 バイト境界にアラインする必要があります。 また、頻繁に使用されるデータを特定のプロセッサのキャッシュ ラインのサイズにアラインすることで、キャッシュ パフォーマンスが向上します。 たとえば、サイズが 32 バイト未満の構造体を定義する場合、その構造体型のオブジェクトが効率的にキャッシュされるように、その構造体を 32 バイトにアラインできます。
# はアラインメント値です。 有効なエントリは、1 ~ 8192 (バイト) の 2 の整数乗 (2、4、8、16、32、64 など) です。 declarator は、aligned として宣言するデータです。
型のアラインメント要件である size_t 型の値を返す方法については、「__alignof」を参照してください。 64 ビット プロセッサを対象としたときにアラインされていないポインターを宣言する方法については、「__unaligned」を参照してください。
__declspec(align(#)) は、struct、union、または class を定義するときや、変数を宣言するときに使用できます。
コンパイラは、コピーまたはデータ変換の処理中に、データのアラインメント属性の保持を保証したり、保持しようとしません。 たとえば、memcpy は、__declspec(align(#)) で宣言された構造体を任意の場所にコピーできます。 通常のアロケーター (malloc など)、C++ 演算子 new、および Win32 アロケーターが返すメモリは、__declspec(align(#)) 構造体または構造体の配列に対して通常は適切にアラインされないことに注意してください。 コピーまたはデータ変換の処理先が正しくアラインされることを保証するには、_aligned_malloc を使用するか、または独自のアロケーターを記述します。
関数パラメーターのアラインメントは指定できません。 アラインメント属性を持つデータがスタック上で値によって渡されるときに、そのアラインメントは呼び出し規約によって制御されます。 呼び出された関数でデータのアラインメントが重要な場合は、使用する前にパラメーターを正常にアラインされたメモリにコピーします。
__declspec(align(#)) がない場合、Visual C++ はターゲット プロセッサおよびデータのサイズに基づいて、自然な境界にデータをアラインします。32 ビット プロセッサでは 4 バイト境界、64 ビット プロセッサでは 8 バイト境界にアラインします。 クラスまたは構造体のデータは、自然なアラインメントと現在のパッキング設定 (#pragma pack または /Zp コンパイラ オプションから) のうちの最小値でクラスまたは構造体内でアラインされます。
__declspec(align(#)) を使用する例を次に示します。
__declspec(align(32)) struct Str1{
int a, b, c, d, e;
};
この型には、32 バイト アラインメント属性が設定されました。 これは、すべての静的および自動インスタンスが 32 バイト境界で開始されることを意味します。 この型でメンバーとして宣言された追加の構造体の型は、この型のアラインメント属性を保持します。つまり、Str1 の構造体の要素には、32 以上のアラインメント属性が設定されます。
sizeof(struct Str1) が 32 と等しいことに注意してください。 このため、Str1 オブジェクトの配列が作成されて、配列のベース アドレスが 32 バイトでアラインされる場合、配列の各メンバーも 32 バイトでアラインされます。 ベース アドレスが動的メモリで適切にアラインされた配列を作成するには、_aligned_malloc を使用するか、または独自のアロケーターを記述します。
構造体の sizeof 値は、最後のメンバーのオフセットにメンバーのサイズを加え、最大サイズのメンバーのアラインメント値の倍数か、構造体全体のアラインメント値のどちらか大きい方に切り上げた値です。
コンパイラは、構造体のアラインメントにこれらの規則を使用します。
__declspec(align(#)) でオーバーライドしない限り、スカラー構造体のメンバーのアラインメントは、そのサイズと現在のパッキング設定の最小値になります。
__declspec(align(#)) でオーバーライドされない限り、構造体のアラインメントは、そのメンバーの個々のアラインメントの最大値になります。
構造体のメンバーが配置される、親の構造体の先頭からのオフセットは、アラインメントの最小倍数であり、かつ前のメンバーの最後のオフセットに等しいかそれよりも大きくなります。
構造体のサイズは、アラインメントの最小倍数であり、かつ最後のメンバーの最後のオフセットに等しいかそれよりも大きくなります。
__declspec(align(#)) では、単にアラインメントの制限が多くなることがあります。
詳細については、次のトピックを参照してください。
align の例
__declspec(align(#)) を使用した新しい型の定義
スレッド ローカル ストレージのデータのアライン
データのパッキングでの align の動作
構造体の配置例 (x64 固有)
align の例
次の例では、__declspec(align(#)) がデータ構造体のサイズとアラインメントにどのような影響を与えるかを示します。 この例では、次の定義を前提とします。
#define CACHE_LINE 32
#define CACHE_ALIGN __declspec(align(CACHE_LINE))
次の例では、S1 構造体は __declspec(align(32)) を使用して定義されます。 変数定義または他の型宣言で S1 を使用すると、すべて 32 バイトでアラインされます。 sizeof(struct S1) は 32 を返し、S1 では 4 つの整数の保持に必要な 16 バイトの後に 16 バイトのパディングが挿入されます。 各 int メンバーには 4 バイトのアラインメントが必要ですが、構造体自体のアラインメントは 32 として宣言されています。 そのため、全体的なアラインメントは 32 になります。
struct CACHE_ALIGN S1 { // cache align all instances of S1
int a, b, c, d;
};
struct S1 s1; // s1 is 32-byte cache aligned
次の例では、sizeof(struct S2) は 16 を返します。これはメンバーのサイズの合計と一致します。この値が最大アラインメント要件の倍数 (8 の倍数) になっているためです。
__declspec(align(8)) struct S2 {
int a, b, c, d;
};
次の例では、sizeof(struct S3) は 64 を返します。
struct S3 {
struct S1 s1; // S3 inherits cache alignment requirement
// from S1 declaration
int a; // a is now cache aligned because of s1
// 28 bytes of trailing padding
};
次の例では、a で自然なアラインメントが行われる (この場合は 4 バイトにアラインされる) ことに注意してください。 ただし、S1 は 32 バイトでアラインする必要があります。 s1 がオフセット 32 で開始するように、a の次の 28 バイトがパディングされます。 次に、S4 は S1 のアラインメント要件を継承します。そのアラインメントが構造体に必要な最大アラインメントであるためです。 sizeof(struct S4) は 64 を返します。
struct S4 {
int a;
// 28 bytes padding
struct S1 s1; // S4 inherits cache alignment requirement of S1
};
次の 3 つの変数宣言も __declspec(align(#)) を使用します。 それぞれの場合で、変数は 32 バイトでアラインする必要があります。 配列の場合、配列の各メンバーではなく、配列のベース アドレスが 32 バイトでアラインされます。 配列の各メンバーの sizeof 値は __declspec(align(#)) の使用による影響を受けません。
CACHE_ALIGN int i;
CACHE_ALIGN int array[128];
CACHE_ALIGN struct s2 s;
配列の各メンバーをアラインするには、次のようなコードを使用する必要があります。
typedef CACHE_ALIGN struct { int a; } S5;
S5 array[10];
次の例では、構造体自体のアラインメントと最初の要素のアラインメントによる影響が同じであることに注意してください。
CACHE_ALIGN struct S6 {
int a;
int b;
};
struct S7 {
CACHE_ALIGN int a;
int b;
};
S6 と S7 では、アラインメント、割り当て、サイズの属性が同じです。
次の例では、a、b、c、および d の開始アドレスのアラインメントは、それぞれ 4、1、4、および 1 です。
void fn() {
int a;
char b;
long c;
char d[10]
}
メモリがヒープ上に割り当てられる場合のアラインメントは、どの割り当て関数が呼び出されるかによって異なります。 たとえば、malloc を使用する場合、結果はオペランドのサイズによって決まります。 arg >= 8 の場合、返されるメモリは 8 バイトでアラインメントされます。 arg < 8 の場合、返されるメモリのアラインメントは arg より小さい最初の 2 の累乗値になります。 たとえば、malloc(7) を使用すると、アラインメントは 4 バイトになります。
__declspec(align(#)) を使用した新しい型の定義
型のアラインメントを定義できます。
たとえば、次のように struct のアラインメント値を定義できます。
struct aType {int a; int b;};
typedef __declspec(align(32)) struct aType bType;
これで、aType と bType は同じサイズ (8 バイト) ですが、bType 型の変数は 32 バイトでアラインされます。
スレッド ローカル ストレージのデータのアライン
__declspec(thread) 属性を使用して作成され、イメージ内の TLS セクションに配置された静的なスレッド ローカル ストレージ (TLS: Thread-Local Storage) は、通常の静的データとまったく同じようにアラインメントされます。 TLS データを作成するために、オペレーティング システムは、TLS セクションのサイズのメモリを割り当て、TLS セクションのアラインメント属性に従います。
次の例では、アラインされたデータをスレッド ローカル ストレージに配置するさまざまな方法を示します。
// put an aligned integer in TLS
__declspec(thread) __declspec(align(32)) int a;
// define an aligned structure and put a variable of the struct type
// into TLS
__declspec(thread) __declspec(align(32)) struct F1 { int a; int b; } a;
// create an aligned structure
struct CACHE_ALIGN S9 {
int a;
int b;
};
// put a variable of the structure type into TLS
__declspec(thread) struct S9 a;
データのパッキングでの align の動作
/Zp コンパイラ オプションおよび pack プラグマは、構造体メンバーおよび共用体メンバーのデータ パッキングに影響を与えます。 この例では、/Zp と __declspec(align(#)) がどのように連携するかを示しています。
struct S {
char a;
short b;
double c;
CACHE_ALIGN double d;
char e;
double f;
};
次の表に、さまざまな /Zp (または #pragma pack) 値での各メンバーのオフセットと共に、それらの値とオフセットの関係を示します。
変数 |
/Zp1 |
/Zp2 |
/Zp4 |
/Zp8 |
---|---|---|---|---|
a |
0 |
0 |
0 |
0 |
b |
1 |
2 |
2 |
2 |
c |
3 |
4 |
4 |
8 |
d |
32 |
32 |
32 |
32 |
e |
40 |
40 |
40 |
40 |
f |
41 |
42 |
44 |
48 |
sizeof(S) |
64 |
64 |
64 |
64 |
詳細については、「/Zp (構造体メンバーの配置)」を参照してください。
オブジェクトのオフセットは、前のオブジェクトのオフセットと現在のパッキング設定に基づきます。ただし、オブジェクトに __declspec(align(#)) 属性が設定されていない場合です。その場合は、アラインメントは前のオブジェクトのオフセットとオブジェクトの __declspec(align(#)) 値に基づきます。