コンパイラの最適化について


最適化については、数千万の方法があり、一定の法則はないと言えるが、コンピュータのアーキテクチャを十分に理解し、自分が処理したデータの特徴を理解し、コンパイラの動作原理を熟知するなど、いくつかの基本的な法則がある.今日はコンパイラについてのいくつかの見解を話したいと思います.コンパイラは前処理、コンパイル、リンクなどの大まかないくつかの部分に分かれています.次に例を示します.
今日、アセンブリ命令では、アドレスオフセット乗算をいくつか削除することを提案されています.このような頭を働かせる方法は賞賛に値しますが、コンパイル過程の理解が不十分で、認識上のいくつかの偏差をもたらす可能性があります.たとえば、次のようなコマンドがあります.
movq 8*4(%%rsi),%%rax
これは移動データの命令であり、操作対象は128ビットの数であり、ソース操作数のアドレス方式はレジスタ間接アドレスであり、アドレスオフセット量は8*4であり、目的操作数は128ビットの通常レジスタである.明らかに、ソースオペランドにはアドレスオフセット計算のプロセスがあり、そのプロセスは、まず8*4を計算し、32を得た後、32をアドレスレジスタ%%rsiに追加し、その後、アドレスメモリの操作を実行することを一般的に理解している.この理解によれば、当然、以下の最適化が提案されると考えられる.
movq 32(%%rsi),%%rax
このプロセスは8*4=32を計算するプロセスが少なく、最適化の役割を果たすはずですが、果たしてそうでしょうか.答えは否定的で、この置換は最適化作用がなく、逆にコードの可読性が低下する可能性があります.これはなぜですか.なぜなら、オペレータの実行時期が混同されているためです.new/deleteはオペレータで、sizeofはオペレータ+-*/%はオペレータですが、オペレータとは何かを本当に理解していますか.オペレータの操作はコンパイル中に実行され、命令に変換されていません.オペレータの操作は最終的に実行期間の命令に変換されます.例を挙げてみましょう.
以下のC言語関数があると仮定する
int main()
{
    int a;
    int b;
    a = 3 * 5 - 2 + 1;
    b = a + 9 * 6 + 1;
    return 0;
}

実行
gcc -S compile.c 

以下のようにする.sアセンブリコード:
movl $14,-8(%%ebp)
movl -8(%%ebp),%%eax
addl $55,%%eax
movl %%eax,-4(%%ebp)

-8(%%ebp),-4(%%ebp)

それぞれa,b変数のメモリ内のアドレスを表し、1回のコンパイルで次の操作が完了したことは明らかです.
3 * 5 - 2 + 1 = 14
9 * 6 + 1 = 55
言い換えれば、これらの操作は実行期間のコードに変換されていないので、もちろん効率の向上とは関係ありません.
主関数内の値付け操作のコードは、次のようなものです.
a = 14;
b = a + 55;

では、いったいそれらのコードは最終的に実行期間のコードに変換できるのでしょうか.1つの原則は、オペレータの1つの操作オブジェクトがメモリ内の値であることです.例えば、a=14はメモリ変数値を付与する操作であり、最終的には
movl $14,-8(%%ebp)

指令一方、b=a+55では、加算動作は
movl -8(%%ebp),%%eax
addl $55,%%eax
すなわち、メモリ変数aの値を最初に取り、加算を完了する
割り当て操作は次のように変換されます.
movl %%eax,-4(%%ebp)
は、メモリ変数bが存在するアドレスに最終的な合計値を送る.
以上から分かるように,コンパイラの動作原理を理解することは,作業の最適化にとって重要である.