コンパイラ理論において、のぞき穴的最適化(のぞきあなてきさいてきか、)とはごく小さな特定の命令列に対して行われる最適化の一種である。この組は”peephole”または"window"と呼ばれる。のぞき穴的最適化はより短い、または高速な命令に置き換えられる命令を探すことによって行われる。
置換ルール のぞき穴的最適化には一般に以下のような手法が用いられる。
ヌルシーケンス‐無駄な命令の削除 命令の結合‐複数の命令を等価なひとつの命令に置換 代数法則の適用‐代数法則を用いることによる命令の簡略化または並べ替え 特殊ケース命令‐特殊ケースのために設計された命令の使用 アドレスモード操作‐コードの簡略化のためのアドレスモードの使用
この他にも様々なのぞき穴的最適化が存在する。
例
遅い命令を速いものに置換
... aload 1 aload 1 mul ... 上のJavaバイトコードは以下のように置き換えることができる。 ... aload 1 dup mul ... この手ののぞき穴的最適化は命令の効率性を仮定している。実際この場合はdup 命令(複製してそれをスタックトップに積む)がaload X命令(X というローカル変数を読み込みスタックに積む)よりも効率的だと仮定している。
冗長なコードの削除 次に挙げる例では冗長なロード・ストア命令を削除する。 a = b + c; d = a + e; 上のコードを素直に実装すると以下のようになる。MOV b, R0 # bをレジスタにコピー ADD c, R0 # cをレジスタに加算。したがってレジスタの値はb+cになる MOV R0, a # レジスタの値をaにコピー MOV a, R0 # aをレジスタにコピー ADD e, R0 # eをレジスタに加算。したがってレジスタの値はa+e [(b+c)+e]になる MOV R0, d # レジスタの値をdにコピーしかしこのコードは以下のように最適化できる。MOV b, R0 # bをレジスタにコピー ADD c, R0 # cをレジスタに加算。したがってレジスタの値はb+cになる (a) MOV R0, a # レジスタの値をaにコピー ADD e, R0 # eをレジスタに加算。したがってレジスタの値はb+c+e [(a)+e]になる MOV R0, d # レジスタの値をdにコピー
冗長なスタック命令の削除 コンパイラがサブルーチンの呼び出し前にレジスタの値をスタックへと退避させて呼び出し後に復元している場合、連続的なサブルーチンの呼び出しにおいて冗長なスタック命令が発生することがある。
コンパイラが以下のようなZ80向け命令列をプロシージャ呼び出しのたびに生成したとする。 PUSH AF PUSH BC PUSH DE PUSH HL CALL _ADDR POP HL POP DE POP BC POP AF例えばサブルーチン呼び出しが2回ある場合は、以下のようになる。 PUSH AF PUSH BC PUSH DE PUSH HL CALL _ADDR1 POP HL POP DE POP BC POP AF PUSH AF PUSH BC PUSH DE PUSH HL CALL _ADDR2 POP HL POP DE POP BC POP AF一般にすぐ後でPUSHするレジスタをPOPする命令は冗長である。実際に冗長な命令だった場合、のぞき穴的最適化でこの命令は削除される。この例では、冗長な隣り合うPOP・PUSH命令が現れた場合、それらを順に削除していく。冗長なコードをすべて削除すると、最終的に以下のようなコードになる。 PUSH AF PUSH BC PUSH DE PUSH HL CALL _ADDR1 CALL _ADDR2 POP HL POP DE POP BC POP AF
実装 現代的アーキテクチャではさまざまなのぞき穴的最適化が可能であり、コンパイラプログラマーはパターンマッチングアルゴリズムを用いてこれを実装するのが基本的に適切である。
関連項目
Object code optimizers Capex Corporation Superoptimization
参考文献
外部リンク
The copt general-purpose peephole optimizer by Christopher W. Fraser The original paper