Compiler Explorerを使ってアセンブリコードを確認しよう。

最適化をするにはある程度アセンブラの知識があると便利ですが、覚えるのがちょっと大変です。
PCのx86(x64)だけに絞っても非常に命令数が多いし複雑です。
コンパイル結果をアセンブル出力しても、見たい物以外がごちゃごちゃあって最初は探すのが大変です。
ソースとアセンブリのどことどこが対応しているのかの把握も大変です。
そこでこのリアルタイムでアセンブリを出力してくれるこのCompiler Explorerです。
打ったそばから、コンパイルを開始して、コンパイルを通ればすぐに表示してくれます。
コードの対応も色分けされていて非常にわかりやすいですし、コード上の右クリックメニューで対応する場所にスクロール出来ます。
C++、Go、Rustなど言語も色々そろっていて、処理系も複数選べます。
バージョン違いが細かくそろっていて、CPUもx86やx64だけでなくARMや最近話題のRISC-Vなんかもあります。
Intel C++ Compilerも選べるのが嬉しいところ。
もちろんコンパイルオプションも指定できます。
最初は簡単なコードから試してみます。
言語はC++で処理系はx86-64 icc18.0.0を使っています。


intの配列の値をすべて足した値を返しています。


int sum(int arr[], int count) {
    auto sum = 0;
    for (auto i = 0; i < count; i++) {
        sum += arr[i];
    }
    return sum;
}

-O0 最適化なし ハイライト部分がループに対応しています。


sum(int*, int):
        push      rbp
#2.31
        mov       rbp, rsp #2.31
        sub       rsp, 32 #2.31
        mov       QWORD PTR [-24+rbp], rdi                      #2.31
        mov       DWORD PTR [-16+rbp], esi                      #2.31
        mov       DWORD PTR [-32+rbp], 0                        #3.14
        mov       DWORD PTR [-28+rbp], 0                        #4.16
..B1.2:                         # Preds ..B1.3 ..B1.1
        mov       eax, DWORD PTR [-28+rbp]                      #4.21
        mov       edx, DWORD PTR [-16+rbp]                      #4.25
        cmp       eax, edx #4.25
        jge       ..B1.4        # Prob 50%                      #4.25
        mov       eax, DWORD PTR [-28+rbp]                      #5.19
        movsxd    rax, eax #5.15
        imul      rax, rax, 4 #5.15
        add       rax, QWORD PTR [-24+rbp]                      #5.15
        mov       eax, DWORD PTR [rax]                          #5.15
        add       eax, DWORD PTR [-32+rbp]                      #5.8
        mov       DWORD PTR [-32+rbp], eax                      #5.8
        mov       eax, 1
#4.32
        add       eax, DWORD PTR [-28+rbp]                      #4.32
        mov       DWORD PTR [-28+rbp], eax                      #4.32
        jmp       ..B1.2        # Prob 100%                     #4.32
..B1.4:                         # Preds ..B1.2
        mov       eax, DWORD PTR [-32+rbp]                      #7.11
        leave
#7.11
        ret                 

-O1 コードがかなり短くなりました。
ループ部分では加算、ループ変数のインクリメント、条件の比較、条件ジャンプと四つの命令で構成されています。
データがキャッシュに入っていれば最近のCPUだと一クロックでループ処理できそうですね。


sum(int*, int):
        xor       eax, eax                                      #3.14
        movsxd    rsi, esi                                      #2.31
        xor       edx, edx                                      #4.16
        test      rsi, rsi                                      #4.25
        jle       ..B1.5        # Prob 10%                      #4.25
..B1.3:                         # Preds ..B1.1 ..B1.3
        add       eax, DWORD PTR [rdi+rdx*4]                    #5.8
        inc       rdx                                           #4.32
        cmp       rdx, rsi                                      #4.25
        jl        ..B1.3        # Prob 82%                      #4.25
..B1.5:                         # Preds ..B1.3 ..B1.1
        ret                                                     #7.11

-O3 ベクトル化でかなり長くなっています。 ループ処理の後にベクトルの4要素を足しています。

sum(int*, int):
        xor       eax, eax                                      #3.14
        test      esi, esi                                      #4.25
        jle       ..B1.18       # Prob 50%                      #4.25
        movsxd    r8, esi                                       #4.4
        cmp       r8, 8                                         #4.4
        jl        ..B1.19       # Prob 10%                      #4.4
        mov       rcx, rdi                                      #4.4
        and       rcx, 15                                       #4.4
        test      ecx, ecx                                      #4.4
        je        ..B1.6        # Prob 50%                      #4.4
        test      cl, 3                                         #4.4
        jne       ..B1.19       # Prob 10%                      #4.4
        neg       ecx                                           #4.4
        add       ecx, 16                                       #4.4
        shr       ecx, 2                                        #4.4
..B1.6:                         # Preds ..B1.5 ..B1.3
        mov       edx, ecx                                      #4.4
        lea       r9, QWORD PTR [8+rdx]                         #4.4
        cmp       r8, r9                                        #4.4
        jl        ..B1.19       # Prob 10%                      #4.4
        mov       r9d, esi                                      #4.4
        sub       r9d, ecx                                      #4.4
        and       r9d, 7                                        #4.4
        sub       esi, r9d                                      #4.4
        xor       r9d, r9d                                      #4.4
        movsxd    rsi, esi                                      #4.4
        test      ecx, ecx                                      #4.4
        jbe       ..B1.11       # Prob 9%                       #4.4
..B1.9:                         # Preds ..B1.7 ..B1.9
        add       eax, DWORD PTR [rdi+r9*4]                     #5.8
        inc       r9                                            #4.4
        cmp       r9, rdx                                       #4.4
        jb        ..B1.9        # Prob 82%                      #4.4
..B1.11:                        # Preds ..B1.9 ..B1.7
        movd      xmm0, eax                                     #3.14
..B1.12:                        # Preds ..B1.12 ..B1.11
        paddd     xmm0, XMMWORD PTR [rdi+rdx*4]                 #5.8
        paddd     xmm0, XMMWORD PTR [16+rdi+rdx*4]              #5.8
        add       rdx, 8                                        #4.4
        cmp       rdx, rsi                                      #4.4
        jb        ..B1.12       # Prob 82%                      #4.4
        movdqa    xmm1, xmm0                                    #3.14
        psrldq    xmm1, 8                                       #3.14
        paddd     xmm0, xmm1                                    #3.14
        movdqa    xmm2, xmm0                                    #3.14
        psrlq     xmm2, 32                                      #3.14
        paddd     xmm0, xmm2                                    #3.14
        movd      eax, xmm0                                     #3.14
..B1.14:                        # Preds ..B1.13 ..B1.19
        cmp       rsi, r8                                       #4.4
        jae       ..B1.18       # Prob 9%                       #4.4
..B1.16:                        # Preds ..B1.14 ..B1.16
        add       eax, DWORD PTR [rdi+rsi*4]                    #5.8
        inc       rsi                                           #4.4
        cmp       rsi, r8                                       #4.4
        jb        ..B1.16       # Prob 82%                      #4.4
..B1.18:                        # Preds ..B1.16 ..B1.14 ..B1.1
        ret                                                     #7.11
..B1.19:                        # Preds ..B1.2 ..B1.4 ..B1.6
        xor       esi, esi                                      #4.4
        jmp       ..B1.14       # Prob 100%   

この記事へのコメント

最近のトラックバック