DESC
データ型のサイズにより、2, 4, 8の倍数のアドレスに配置されること
ハードウェア的な制約もあれば
より高速な処理をするためにコンパイラ(リンカ、ローダ)が指示するものもある。
メモリ、CPU 間でやりとりできるデータのサイズは 16bit, 32bit だったりする
整列しておけば 4byte のデータを 1回で転送できる
Alignment をする理由
POINT
変数は宣言順に確保されるとは限らない
ローカル変数は宣言した順に, 配置されない
構造体の各メンバは宣言順だが, Alignment されて、メンバの間には隙間( padding ) が空く場合もある
構造体のサイズが padding されるのは, 各メンバが適切な位置に配置されるため隙間が空いてしまう。
隙間が空くのを防ぐにはメンバの構成を入れ替える。
または padding を明示しておく。
BAD
struct Foo {
int i0;
short s0;
int i1; // int 型は 4 の倍数で配置されるため s0, i1 の間に隙間が空いてしまう。
short s1;
};
// OK
struct Foo {
int i0;
int i1;
short s0;
short s1;
};
// OK
struct Foo {
int i0;
short s0;
char pad[2];
int i1;
short s1;
};
Compiler の指定 で 変更可能( vc: 構造体 mem への alignment )
-> mem , cpu は 32 本の線で結合
-> 可変サイズで, Buffer をもつより, Alignment を守って大きめのサイズにした方が都合がよい場合もある.
// sizeof(S) = 2
// 2 コ で 4 byte
struct S{
short s;
};
// sizeof(C) = 1
// 4 コ で 4 byte
struct C{
char c;
};
// sizeof(S3) = 6
struct S3{
short s0;
short s1;
short s2;
};
x86 系では, double は 8ByteAlignment にすると効率がよい。
Architecture によっては必須になる.
Alignemnt をそろえた, memory の取り方
data = (void *)myMalloc( size + 32 ); // 32 byte Alignemnt
s_data_org = data; // ori 保存
data = (void*)(((u_int)data + 31) & ~31 ); // Alignemnt Macro
POINT
struct cls mem 内が連続しているとは限らない
現実問題として, float, short, char, が struct 内に並ぶ場合は 連続して配置されている
enum も連続していると考えられる
// 12 byte
struct Foo{
char c0; // 1 の倍数で alignment 1
float f0; // 4 の倍数で alignment
char c1; // 1 の倍数で alignment
};
// 残り3byteを埋めないと次の構造体を配置した場合にf0の配置に問題がおきる。
[c0][ ][ ][ ][f0][f0][f0][f0][c1][ ][ ][ ]
// sizeof(Foo) = 8 FILE: main.cpp
struct Foo{
char c1;
char c2;
float f;
};
■ プラグマでアラインメントを指定する
SYNTAX
__declspec( align( # ) ) declarator
コンパイラのプラグマでアラインメントを指定する。
VisualStudio では以下の方法で指定する。
// Vector 構造体を 8 byte の境界の配置するように指示する
__declspec( align(8) ) struct Vector
{
short x;
short y;
short z;
};
typedef struct ALIGN(8) { short vx, vy, vz, pad; } SHORT_VECTOR;
アラインメントを指定すると, アドレスが 8 byte の境界にあることがわかる。
Vector v;
int adr = (int)&v;
adr %= 8;
printf( "addr %d %d" , &v, adr );
POINT
alignment の指定は 2 冪でないとだめ
ERROR C2344: align(12) : アライメントは 2 の累乗でなければなりません。
__declspec( align(12) ) struct SA
■ キャッシュライン(CacheLine)
よく使うデータはキャッシュラインに揃えることで、キャッシュミスが置きにくくなりプログラムの速度があがる。
cacheは、16bytesごとの塊xN個で構成されている。
この16bytesの塊1つを、1lineという。
lineが何個あるかは、cacheサイズをlineサイズで割れば計算できる。
1KB / 16 = 64なので、このcacheには64line存在する
POINT
良く使うデータや、連続して使うデータが同じcache lineに割り当てられてしまうと、cache missが頻発してしまいます。
■ アラインメントを指定しない場合
Object はまず固有のサイズに対するアラインメントをもつ。
int は 4byte 境界に、 double は 8byte 境界に配置される。
int は 4 で割り切れる場所にないといけない。
クラス内にあるメンバオブジェクトは、オブジェクトのアラインメントのうち最小の値の位置にアラインメントされる。
struct Test {
char c;
short s; // 2byte 境界にアラインメントするため, c と s 間に 1byte のパディングがはいる
};
2. (push 8) は 1 の alignment の設定と比較してより小さい alignment が優先される
3. -> 適当に詰め物をする
// 4 Byte 境界に押し込め と命令
#pragma pack(push, 4) -> 12 byte
struct Foo
{
char B; // 1 byte alignment
double W; // 4 byte alignment( 8 より優先 )
char H; // 1 byte alignment
short s; // 2 byte alignment
};
#pragma pack(pop)
POINT
プリミティブな値は、min( szObj, Alignment )の倍数のアドレスに配置する
一回で CPU に data を渡せるように箱に詰め物をするのが Alignment
構造体は 2冪サイズにするべき ( 最低でもキャッシュライン倍数にする )