75326


**実行環境**  
CPU ryzen 5 3600  
OS windows10 64bit上のvirtual boxでUbuntu 18.04.5 LTSを起動  
アセンブラ nasm  
オプション -f elf64  
コンパイラ clang  
オプション -O0

[8バイトにアライメントされていないchar
*型を整数型にキャストした時エラーが起きないのはなぜですか？](https://ja.stackoverflow.com/questions/75105/8%e3%83%90%e3%82%a4%e3%83%88%e3%81%ab%e3%82%a2%e3%83%a9%e3%82%a4%e3%83%a1%e3%83%b3%e3%83%88%e3%81%95%e3%82%8c%e3%81%a6%e3%81%84%e3%81%aa%e3%81%84char-%e5%9e%8b%e3%82%92%e6%95%b4%e6%95%b0%e5%9e%8b%e3%81%ab%e3%82%ad%e3%83%a3%e3%82%b9%e3%83%88%e3%81%97%e3%81%9f%e6%99%82%e3%82%a8%e3%83%a9%e3%83%bc%e3%81%8c%e8%b5%b7%e3%81%8d%e3%81%aa%e3%81%84%e3%81%ae%e3%81%af%e3%81%aa%e3%81%9c%e3%81%a7%e3%81%99%e3%81%8b)  
過去の質問↑で教えていただいたサイトにアライメント制約のないx86でもアラインされていないデータの読み出しはメモリアクセス回数が増えて実行速度が落ちるという趣旨の記述がありました。↓  
[http://www5d.biglobe.ne.jp/~noocyte/Programming/Alignment.html#AlignmentTolerantCPU](http://www5d.biglobe.ne.jp/%7Enoocyte/Programming/Alignment.html#AlignmentTolerantCPU)

そこで下記のようなコードを作成し、`time`コマンドで速度を図って試してみたのですが、実行速度に差がありませんでした  
これは意図したとおりの検証になっているのでしょうか？（[rdi]がキャッシュされていて正しく検証できていないなど・・・）

[code]

    section .text
    global test
    
    test:
        xor rax, rax
        mov rax,  [rdi]
        mov rax,  [rdi]
        mov rax,  [rdi]
        mov rax,  [rdi]
        mov rax,  [rdi]
        mov rax,  [rdi]
        mov rax,  [rdi]
        mov rax,  [rdi]
        mov rax,  [rdi]
        mov rax,  [rdi]
        mov rax,  [rdi]
        mov rax,  [rdi]
        mov rax,  [rdi]
        mov rax,  [rdi]
        mov rax,  [rdi]
        mov rax,  [rdi]
        mov rax,  [rdi]
        mov rax,  [rdi]
        mov rax,  [rdi]
        mov rax,  [rdi]
        ret
    
[/code]

[code]

    #include <stdio.h>
    #include <stdint.h>
    #include <unistd.h>
    #include <limits.h>
    
    size_t test(char *s);
    
    int main()
    {
        char *s = "123456789abcdefghijklmn";
        uintptr_t i = (uintptr_t)s % 8;
    
        s += ((8 - i) % 8); //アライメントあり
        //s += !((8 - i) % 8); //アライメントなし
        printf("%p\n", s);
    
        long l = 0;
        while (l++ < INT_MAX)
            test(s);
    }
    
[/code]

これまでの自分の認識では  
仮に以下のようなメモリの状態だった場合  
（'|'はワード境界）

[code]

    | * * '2' '3' '4' '5' '6' '7' | '8' '9'
    
    
[/code]

'2'のアドレスに対してロード命令で8byte raxに読み込んだ場合  
raxの状態は

[code]

    | * * '2' '3' '4' '5' '6' '7' |
    
    
[/code]

上記の様になる想定だった（CPUはワードサイズの倍数単位のアドレスでしかメモリにアクセスできないと思っていました）のですが、実際にコードを書いてみるとraxの下位1バイトは'2'になっていました  
これまでは、アライメント制約がなくても実行速度上はアラインされていた方がいいのだろうと思っていましたが、もしかしてx86ではアライメントを気にする必要はほぼないのでしょうか？

例えば下記glibcのstrlenの実装では、最初にアライメントを揃える処理をしていますが、x86においてはこの処理は全く必要ないのでしょうか？  
<https://github.com/lattera/glibc/blob/master/string/strlen.c>


CC BY-SA 4.0
2021-04-16T08:34:29.383
75325
2021-04-16T22:55:43.353
44647
question
c assembly memory
x86では、ワードサイズより大きいアライメントされていないデータを読み出す場合、メモリアクセス回数は増えますか？


> これは意図したとおりの検証になっているのでしょうか？（[rdi]がキャッシュされていて正しく検証できていないなど・・・）

アセンブリコードに関しては、その(疑問の)通り(で、検証になっていない)でしょうね。  
同一アドレスのメモリを読み取っているだけなのでキャッシュに収まったままでしょうし、もっと言えば変化が無い同一の処理が並んでいるので実行がスキップされている可能性も考えられます。

例えば`rdi`は順次インクリメントして、`mov rax`ではなく`add rax`にする必要があるのでは？

* * *

>
> アライメント制約がなくても実行速度上はアラインされていた方がいいのだろうと思っていましたが、もしかしてx86ではアライメントを気にする必要はほぼないのでしょうか？

原理的にはアクセス回数の増加は発生していますが、多段階のキャッシュとか先読み/投機/パイプライン等の様々な手法によりハードウェアの高速化が図られています。1画面にも満たない一筆書きの20行程度の処理では違いがあったとしてもバラツキや誤差の範囲と変わらないかもしれません。

前回の回答にも紹介した通り、一部の例外的な命令/データや、性能競争とかスーパーコンピュータ的な用途のアプリケーションでない限り、アライメントを気にする必要はほぼないでしょう。

* * *

と、ここまで書いておいてひっくり返すようですが、コンパイラ(アセンブラも)ではデフォルトでそれなりのアライメント調節を行っています。  
C++の標準仕様とかリリース用ビルド時の最適化などが関係しているのでしょう。

[align (C++)](https://docs.microsoft.com/ja-jp/cpp/cpp/align-
cpp?view=msvc-160)  
[Alignment](https://docs.microsoft.com/ja-jp/cpp/cpp/alignment-cpp-
declarations?view=msvc-160)

そういうことも含めて、大部分のアプリケーション/ライブラリ等は、細かいメモリアクセスのサイクル差ではなく、アルゴリズムとかの方を気にした方が良いと思われます。


CC BY-SA 4.0
2021-04-16T09:06:18.777
75326
2021-04-16T13:08:07.027
2021-04-16T13:08:07.027
26370
26370
75325
answer


既に解決済みなので、参考までに [CLFLUSH - Flush Cache
Line](https://hjlebbink.github.io/x86doc/html/CLFLUSH.html) を使って `CPU
cache(L1/L2/L3)` を flush してみます。

**実行環境**

[code]

    $ LC_ALL=C lscpu | grep -E '(^(Architecture|Model name)|cache:)'
    Architecture:          x86_64
    Model name:            Intel(R) Core(TM) i5-8500T CPU @ 2.10GHz
    L1d cache:             192 KiB
    L1i cache:             192 KiB
    L2 cache:              1.5 MiB
    L3 cache:              9 MiB
    
    $ lscpu | grep -Po 'clflush\S*'
    clflush
    clflushopt
    
    $ uname -srmo
    Linux 5.8.0-50-generic x86_64 GNU/Linux
    
    $ lsb_release -d
    Description:    Ubuntu 20.10
    
    $ gcc --version
    gcc (Ubuntu 10.2.0-13ubuntu1) 10.2.0
    
[/code]

**実行コード**

`CLFLUSH` ですが、実際には `__mm_clflush()` を使います。

**`main.c`**

[code]

             :
    #include <stdalign.h>
    #include <x86intrin.h>
    
    int main()
    {
      alignas(8) char s[] = "123456789abcdefghijklmn";
      char *p = s;        // アライメントあり
      //char *p = s + 1;  // アライメントなし
    
      long l = 0;
      while (l++ < INT_MAX) {
        test(p);
        _mm_clflush(s);
      }
    }
    
[/code]

**実行時間**

以下、alignment あり・なし、CPU cache を flush しない・する場合での実行時間です。

[code]

    $ gcc -O0 -o main main.c test.o
    $ time taskset -c 5 ./main
    
    ## alignment あり(with cache)
    ##
    real    0m9.306s
    user    0m9.301s
    sys     0m0.005s
    
    ## alignment あり(flush cache)
    ##
    real    3m34.985s
    user    3m34.968s
    sys     0m0.014s
    
    ## alignment なし(with cache)
    ##
    real    0m9.356s
    user    0m9.352s
    sys     0m0.005s
    
    ## alignment なし(flush cache)
    ##
    real    3m39.436s
    user    3m39.422s
    sys     0m0.010s
    
[/code]


CC BY-SA 4.0
2021-04-16T16:43:56.147
75329
2021-04-16T22:55:43.353
2021-04-16T22:55:43.353
75325
answer