アンアラインメントデータアクセスについて
- メモリにデータを格納する際、通常はアラインメントに沿って展開される。ワード単位以上のデータを格納する場合、その開始アドレスがワード単位の整数倍になっていないと、メモリフェッチが余分に発生してスループットが落ちるか、アラインメント違反によってエラーが発生するか、アクセスしたデータが異常となり期待していない挙動を引き起こす。
- x86では、アラインメントに沿っていないアドレスにアクセスする場合、メモリフェッチが余分に発生するが、アラインメント違反は起こらない。ARM/Linuxでは、アラインメント違反が発生するが、/proc/cpu/alignmentに設定されている値に従って結果として発生する挙動が変わる。
0 | 例外を無視 |
1 | 例外をdmesgに出力 |
2 | 例外を捕捉して正常にアクセスする |
3 | 1+2 |
4 | 例外を受理してバスエラーを起こす |
5 | 1+4 |
c.f. Linux Kernel Documentation :: arm : mem_alignment
- 構造体
struct
は各フィールドの中で最大のアラインメントに併せてパディングを入れてメモリを確保する。ただし、__attribute__((packed))
を指定した場合はパディングを入れない。なお、共用体union
は各フィールドでメモリを共有する。 - gcc/clangでは
__attribute__((aligned(byte)))
によってアラインメントをマニュアルで調整することができる。C++11(C++0x)では言語の機能として、alignas(byte)が導入されている。
c.f. ホイール欲しい ハンドル欲しい » C++11 alignas/alignof メモリアライメント
$ cat align.c #include <stdio.h> int main(int argc, char* argv[]) { char a[16] = {"abcd"}; unsigned int *b,*c,*d; b = (unsigned int *)(a); c = (unsigned int *)(a+2); d = (unsigned int *)(a+3); printf("char a %s\n",a); printf("int b %u\n",*b); printf("int c %u\n",*c); printf("int d %u\n",*d); printf("char a addr %p\n",a); printf("int b addr %p\n",b); printf("int c addr %p\n",c); printf("int d addr %p\n",d); struct s1 { char i; } s1; struct s2 { int j; } s2; struct s3 { char i; int j; } s3; struct s4 { char i; int j; } __attribute__((packed)) s4; union u4 { char i; int j; } u4; struct s5 { char i; __attribute__((aligned(8))) int j; } s5; printf("s1 size %zu\n",sizeof(s1)); printf("s2 size %zu\n",sizeof(s2)); printf("s3 size %zu\n",sizeof(s3)); printf("s4 size %zu\n",sizeof(s4)); printf("u4 size %zu\n",sizeof(u4)); printf("s5 size %zu\n",sizeof(s5)); printf("s3 addr %p\n",&s3); printf("s3.i addr %p\n",&s3.i); printf("s3.j addr %p\n",&s3.j); printf("s4 addr %p\n",&s4); printf("s4.i addr %p\n",&s4.i); printf("s4.j addr %p\n",&s4.j); printf("u4 addr %p\n",&u4); printf("u4.i addr %p\n",&u4.i); printf("u4.j addr %p\n",&u4.j); printf("s5 addr %p\n",&s5); printf("s5.i addr %p\n",&s5.i); printf("s5.j addr %p\n",&s5.j); s3.i = 'a'; s3.j = 65535; printf("s3.i %c\n",s3.i); printf("s3.j %d\n",s3.j); s4.i = 'a'; s4.j = 65535; printf("s4.i %c\n",s4.i); printf("s4.j %d\n",s4.j); return 0; } $ ./a.out char a abcd int b 1684234849 int c 25699 int d 100 char a addr 0x7fff504e5130 int b addr 0x7fff504e5130 int c addr 0x7fff504e5132 int d addr 0x7fff504e5133 s1 size 1 s2 size 4 s3 size 8 s4 size 5 u4 size 4 s5 size 16 s3 addr 0x7fff504e50f0 s3.i addr 0x7fff504e50f0 s3.j addr 0x7fff504e50f4 s4 addr 0x7fff504e50e8 s4.i addr 0x7fff504e50e8 s4.j addr 0x7fff504e50e9 u4 addr 0x7fff504e50e0 u4.i addr 0x7fff504e50e0 u4.j addr 0x7fff504e50e0 s5 addr 0x7fff519cb0b0 s5.i addr 0x7fff519cb0b0 s5.j addr 0x7fff519cb0b8 s3.i a s3.j 65535 s4.i a s4.j 65535 $ uname -a Darwin MBA.local 14.5.0 Darwin Kernel Version 14.5.0: Wed Jul 29 02:26:53 PDT 2015; root:xnu-2782.40.9~1/RELEASE_X86_64 x86_64
Thanks to
Note : []演算子について
- 配列
array
と[]演算子に与える添字i
において、array
とi
は交換可能である。つまり、array[i]
とi[array]
は等価である。これは[]演算子をコンパイラが*(array+i)
に変換しているためである。
int i[4] = {1,2,3,4}; int j=1; printf("i[j] %d\n",i[j]); printf("j[i] %d\n",j[i]); printf("1[i] %d\n",1[i]); printf("*(i+j) %d\n",*(i+j)); ~~~ i[j] 2 j[i] 2 1[i] 2 *(i+j) 2
32ビットコンピュータをやさしく語る はじめて読む486 (アスキー書籍)
- 作者: 蒲地輝尚
- 出版社/メーカー: KADOKAWA / アスキー・メディアワークス
- 発売日: 2014/10/21
- メディア: Kindle版
- この商品を含むブログを見る
- 作者: 川合秀実
- 出版社/メーカー: 毎日コミュニケーションズ
- 発売日: 2006/03/01
- メディア: 単行本
- 購入: 36人 クリック: 735回
- この商品を含むブログ (301件) を見る