LoginSignup
0

More than 3 years have passed since last update.

x64 CPUのアライメントフォールトのペナルティを調べた

Last updated at Posted at 2020-04-23

この記事はなんぞや

別の記事を書くにあたって「そういえば、ずっと昔に x86 でのアライメントフォールト(alignment fault)のペナルティの話を聞いたけど、今でも影響あるんかなー」と思って調べてみた。

結論: ある

前提

(追記 2020/04/24) すごい、このページ分かりやすいです!
http://www5d.biglobe.ne.jp/~noocyte/Programming/Alignment.html

(追記 2020/07/10) 再現できない環境もあったので、いじりました。もはや、どうやればアライメントフォールトを発生させられるか、といったゲームになっているので、普通はまったく意識しなくていいですね……。

テストコード

ALIGN 構造体の paddingunsigned long long (8バイト)なので、その次の char[9] の先頭も8の倍数バイトでアラインされていることが期待できます。

ここで char[9]align.base を、先頭から8バイト分 unsigned long long として扱うポインタ *p を用意します。ここからポインタをぐりっといじって *p の先頭を1バイトずらします。
これでアライメントフォールトが発生するはずです。

align.c
#include <stdio.h>

struct ALIGN {
    unsigned long long  padding;
    char                base[9];
};

int main(int argc, char **argv)
{
    unsigned long long  i = 0L;
    unsigned long long  loop_count = 1000000000L;
    unsigned long long   *p;

    struct ALIGN        align;

    if ( argc == 1 ) {
        p = (unsigned long long *) (((char *) &align.base));
        printf( "aligned: %p\n", p );
    }
    else {
        p = (unsigned long long *) (((char *) &align.base + 1));
        printf( "not aligned: %p\n", p );
    }

    *p = 0;
    for ( i = 0; i < loop_count; i++ )
        *p = *p + 1;

    return 0;
}

実行結果

引数を付けずに実行するとアラインされたまま、なにか引数を付けるとアライメントをずらした unsigned long long に代入するのを10億回繰り返します。

とりあえず実行してポインタの値を確認すると、アドレスが奇数になっているので、アライメントが合っていないのが分かります。

$ gcc alignment_fault.c
$
$ ./a.out
aligned: 0x7ffee1cf0a48
$ ./a.out 1
not aligned: 0x7ffee4c09a39

実行時間を計ります。

$ time ./a.out
aligned: 0x7ffeead84a48
./a.out  1.14s user 0.00s system 99% cpu 1.141 total
$ time ./a.out
aligned: 0x7ffee36f6a48
./a.out  1.14s user 0.00s system 99% cpu 1.145 total
$ time ./a.out
aligned: 0x7ffee8004a48
./a.out  1.15s user 0.00s system 99% cpu 1.152 total

$ time ./a.out 1
not aligned: 0x7ffeeba19a39
./a.out 1  2.18s user 0.00s system 99% cpu 2.187 total
$ time ./a.out 1
not aligned: 0x7ffee1d28a39
./a.out 1  2.20s user 0.00s system 99% cpu 2.201 total
$ time ./a.out 1
not aligned: 0x7ffee0aefa39
./a.out 1  2.21s user 0.00s system 99% cpu 2.215 total

このように、2倍弱の遅れが生じました。ほほー。
古いマシンで試したことはないから、昔に比べてどうだとかは分からないですが。

追記(2020/07/10)

環境によって差が出たり出なかったりすると思って gcc のマニュアルを眺めていたんですが、こんなことが書いてありました。

-malign-data=type
Control how GCC aligns variables. Supported values for type are ‘compat’ uses increased alignment value compatible uses GCC 4.8 and earlier, ‘abi’ uses alignment value as specified by the psABI, and ‘cacheline’ uses increased alignment value to match the cache line size. ‘compat’ is the default.

ひい。gcc は何もしなくてもアライメントを合うように設定するようです。アドレスを表示させたときに奇数になったのはなんでだ……。

あとスタックに乗るときにもデフォルトでは調整されるらしい1ので、ヒープに乗せるように変更しました。

align2.c
#include <stdio.h>

struct ALIGN {
    unsigned long long  padding;
    char                base[9];
};

struct ALIGN align;

int main(int argc, char **argv)
{
    unsigned long long  i = 0L;
    unsigned long long  loop_count = 1000000000L;
    unsigned long long   *p;

    if ( argc == 1 ) {
        p = (unsigned long long *) (((char *) &align.base));
        printf( "aligned: %p\n", p );
    }
    else {
        p = (unsigned long long *) (((char *) &align.base + 1));
        printf( "not aligned: %p\n", p );
    }

    *p = 0;
    for ( i = 0; i < loop_count; i++ )
        *p = *p + 1;

    return 0;
}

gcc のオプションに -malign-data=abi を付けます。
あと、gcc は最適化オプションを指定しないときは -O0 とみなすのですが、最適化を無効にするために2明示的に付け足しました。

gcc -O0 -malign-data=abi align2.c

結果は以下の通り。

$ time ./a.out
aligned: 0x601038
./a.out  2.25s user 0.00s system 99% cpu 2.254 total
$ time ./a.out
aligned: 0x601038
./a.out  2.25s user 0.00s system 99% cpu 2.251 total
$ time ./a.out
aligned: 0x601038
./a.out  2.26s user 0.00s system 99% cpu 2.266 total

$ time ./a.out 1
not aligned: 0x601039
./a.out 1  2.72s user 0.00s system 99% cpu 2.720 total
$ time ./a.out 1
not aligned: 0x601039
./a.out 1  2.72s user 0.00s system 99% cpu 2.719 total
$ time ./a.out 1
not aligned: 0x601039
./a.out 1  2.71s user 0.00s system 99% cpu 2.712 total

期待する結果出ました。

というかここまで無理くりやらないとダメなくらい、コンパイラは当たり前にアライメントの最適化をするってことですな……。

ちなみに -O(-O1) だとどちらも 0.38 秒ぐらいになります。(ちなみにちなみに -O2 だと 0.001 秒ぐらいになります)


  1. -mstackrealign Realign the stack at entry. On the x86, the -mstackrealign option generates an alternate prologue and epilogue that realigns the run-time stack if necessary. (https://gcc.gnu.org/onlinedocs/gcc/x86-Options.html

  2. コメントで教えていただいたのですが、-O0 は最適化を完全に無効にするわけではないとのこと。 gcc -Q -O0 --help=optimizers-O0 のときの最適化内容が分かるそうです。ありがとうございます。@fujitanozomu 

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0