align
(C++)
Visual Studio 2015 以降では、alignas
指定子 (C++11) を使用してアライメントを制御します。 詳細については、「Alignment」を参照してください。
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++ operator new
、Win32 アロケーターなど) から返されるメモリは、__declspec(align(#))
構造体または構造体の配列に対してアラインされていません。 コピーまたはデータ変換操作の同期先が正しくアラインされるようにするには、_aligned_malloc
を使用します。 または、独自のアロケーターを記述します。
関数パラメーターのアラインメントは指定できません。 アラインメント属性を含むデータをスタック上の値によって渡す場合、そのアラインメントは呼び出し規則によって制御されます。 呼び出された関数でデータのアラインメントが重要な場合は、使用する前にパラメーターを正常にアラインされたメモリにコピーします。
__declspec(align(#))
がない場合、コンパイラはターゲット プロセッサおよびデータのサイズに基づいて、自然な境界にデータをアラインします。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 バイトでアラインする必要があります。 a
がオフセット 32 で開始するように、s1
の次の 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(#))
値に基づきます。
Microsoft 固有の仕様はここまで