コンパイラやプロセッサの最適化戦略により、アルゴリズムの複雑さのクラスが明らかに変化している例を探しています。
int main(void) { exit(0); };
コンパイラやプロセッサの最適化戦略により、アルゴリズムの複雑さのクラスが明らかに変化している例を探しています。
int main(void) { exit(0); };
回答:
コマンドラインに入力された数値の2乗を出力する単純なプログラムを見てみましょう。
#include <stdio.h>
int main(int argc, char **argv) {
int num = atoi(argv[1]);
printf("%d\n",num);
int i = 0;
int total = 0;
for(i = 0; i < num; i++) {
total += num;
}
printf("%d\n",total);
return 0;
}
ご覧のとおり、これはO(n)計算であり、何度もループしています。
これをgcc -S
1つでコンパイルすると、次のようなセグメントが取得されます。
LBB1_1:
movl -36(%rbp), %eax
movl -28(%rbp), %ecx
addl %ecx, %eax
movl %eax, -36(%rbp)
movl -32(%rbp), %eax
addl $1, %eax
movl %eax, -32(%rbp)
LBB1_2:
movl -32(%rbp), %eax
movl -28(%rbp), %ecx
cmpl %ecx, %eax
jl LBB1_1
これで、追加が行われたこと、ループの比較とジャンプバックを確認できます。
を使用してコンパイルを行いgcc -S -O3
、printfの呼び出し間のセグメントを最適化します。
callq _printf
testl %ebx, %ebx
jg LBB1_2
xorl %ebx, %ebx
jmp LBB1_3
LBB1_2:
imull %ebx, %ebx
LBB1_3:
movl %ebx, %esi
leaq L_.str(%rip), %rdi
xorb %al, %al
callq _printf
代わりに、ループも追加もありません。代わりにimull
、それ自体で数を乗算する呼び出しがあります。
コンパイラはループと内部の数学演算子を認識し、適切な計算に置き換えました。
これにはatoi
、番号を取得するための呼び出しが含まれていることに注意してください。数値がすでにコードに存在する場合、コンパイラーは、C#とCでのsqrtのパフォーマンスの比較sqrt(2)
(定数)がループ全体で1,000,000回合計されたことを示すように、実際の呼び出しを行うのではなく、値を事前に計算します。
テールコールの最適化により、スペースの複雑さが軽減される場合があります。たとえば、TCOを使用しない場合、このwhile
ループの再帰的な実装はワーストケースのスペースの複雑さを持ちますが、Ο(#iterations)
TCOを使用すると、ワーストケースのスペースの複雑さはΟ(1)
次のようになります。
// This is Scala, but it works the same way in every other language.
def loop(cond: => Boolean)(body: => Unit): Unit = if (cond) { body; loop(cond)(body) }
var i = 0
loop { i < 3 } { i += 1; println(i) }
// 1
// 2
// 3
// E.g. ECMAScript:
function loop(cond, body) {
if (cond()) { body(); loop(cond, body); };
};
var i = 0;
loop(function { return i < 3; }, function { i++; print(i); });
これには一般的なTCOも必要ありません。必要なのは、非常に狭い特殊なケース、つまり直接テール再帰の排除だけです。
どうなるか非常にコンパイラの最適化は、単に複雑性クラスを変更しますが、実際には完全にアルゴリズムを変更していないところけれども面白いです。
Glorious Glasgow Haskell Compilerは時々これを行いますが、それは私が実際に話していることではなく、それは不正行為のようです。GHCには、ライブラリの開発者がいくつかの単純なコードパターンを検出し、それらを別のコードに置き換えることができる単純なパターンマッチング言語があります。また、Haskell標準ライブラリのGHC実装にはこれらの注釈の一部が含まれているため、非効率であることがわかっている特定の関数の特定の使用法がより効率的なバージョンに書き直されます。
ただし、これらの翻訳は人間が作成したものであり、特定のケース向けに作成されたものです。
A Supercompilerは、人間の入力なしにアルゴリズムを変更することができるかもしれないが、私の知る限りなしの生産レベルのsupercompilerは、これまでに構築されています。