コンテンツにスキップ

英文维基 | 中文维基 | 日文维基 | 草榴社区

「のぞき穴的最適化」の版間の差分

出典: フリー百科事典『ウィキペディア(Wikipedia)』
削除された内容 追加された内容
Kotetttt (会話 | 投稿記録)
m編集の要約なし
Cewbot (会話 | 投稿記録)
m Bot作業依頼: sourceタグをsyntaxhighlightタグに置換 (Category:非推奨のsourceタグを使用しているページ) - log
33行目: 33行目:
a = b + c;
a = b + c;
d = a + e;
d = a + e;
上のコードを素直に実装すると以下のようになる。<source lang="asm">
上のコードを素直に実装すると以下のようになる。<syntaxhighlight lang="asm">
MOV b, R0 # bをレジスタにコピー
MOV b, R0 # bをレジスタにコピー
ADD c, R0 # cをレジスタに加算。したがってレジスタの値はb+cになる
ADD c, R0 # cをレジスタに加算。したがってレジスタの値はb+cになる
40行目: 40行目:
ADD e, R0 # eをレジスタに加算。したがってレジスタの値はa+e [(b+c)+e]になる
ADD e, R0 # eをレジスタに加算。したがってレジスタの値はa+e [(b+c)+e]になる
MOV R0, d # レジスタの値をdにコピー
MOV R0, d # レジスタの値をdにコピー
</source>しかしこのコードは以下のように最適化できる。<source lang="asm">
</syntaxhighlight>しかしこのコードは以下のように最適化できる。<syntaxhighlight lang="asm">
MOV b, R0 # bをレジスタにコピー
MOV b, R0 # bをレジスタにコピー
ADD c, R0 # cをレジスタに加算。したがってレジスタの値はb+cになる (a)
ADD c, R0 # cをレジスタに加算。したがってレジスタの値はb+cになる (a)
46行目: 46行目:
ADD e, R0 # eをレジスタに加算。したがってレジスタの値はb+c+e [(a)+e]になる
ADD e, R0 # eをレジスタに加算。したがってレジスタの値はb+c+e [(a)+e]になる
MOV R0, d # レジスタの値をdにコピー
MOV R0, d # レジスタの値をdにコピー
</syntaxhighlight>
</source>


=== 冗長なスタック命令の削除 ===
=== 冗長なスタック命令の削除 ===
コンパイラがサブルーチンの呼び出し前にレジスタの値をスタックへと退避させて呼び出し後に復元している場合、連続的なサブルーチンの呼び出しにおいて冗長なスタック命令が発生することがある。
コンパイラがサブルーチンの呼び出し前にレジスタの値をスタックへと退避させて呼び出し後に復元している場合、連続的なサブルーチンの呼び出しにおいて冗長なスタック命令が発生することがある。


コンパイラが以下のような[[Z80]]向け命令列をプロシージャ呼び出しのたびに生成したとする。<source lang="asm">
コンパイラが以下のような[[Z80]]向け命令列をプロシージャ呼び出しのたびに生成したとする。<syntaxhighlight lang="asm">
PUSH AF
PUSH AF
PUSH BC
PUSH BC
61行目: 61行目:
POP BC
POP BC
POP AF
POP AF
</source>例えばサブルーチン呼び出しが2回ある場合は、以下のようになる。<source lang="asm">
</syntaxhighlight>例えばサブルーチン呼び出しが2回ある場合は、以下のようになる。<syntaxhighlight lang="asm">
PUSH AF
PUSH AF
PUSH BC
PUSH BC
80行目: 80行目:
POP BC
POP BC
POP AF
POP AF
</source>一般にすぐ後でPUSHするレジスタをPOPする命令は冗長である。実際に冗長な命令だった場合、のぞき穴的最適化でこの命令は削除される。この例では、冗長な隣り合うPOP・PUSH命令が現れた場合、それらを順に削除していく。冗長なコードをすべて削除すると、最終的に以下のようなコードになる。<source lang="asm">
</syntaxhighlight>一般にすぐ後でPUSHするレジスタをPOPする命令は冗長である。実際に冗長な命令だった場合、のぞき穴的最適化でこの命令は削除される。この例では、冗長な隣り合うPOP・PUSH命令が現れた場合、それらを順に削除していく。冗長なコードをすべて削除すると、最終的に以下のようなコードになる。<syntaxhighlight lang="asm">
PUSH AF
PUSH AF
PUSH BC
PUSH BC
91行目: 91行目:
POP BC
POP BC
POP AF
POP AF
</syntaxhighlight>
</source>


== 実装 ==
== 実装 ==

2020年7月5日 (日) 23:10時点における版

コンパイラ理論において、のぞき穴的最適化(のぞきあなてきさいてきか、英語: Peephole optimization)とはごく小さな特定の命令列に対して行われる最適化の一種である。この組は”peephole”または"window"と呼ばれる。のぞき穴的最適化はより短い、または高速な命令に置き換えられる命令を探すことによって行われる。

置換ルール

のぞき穴的最適化には一般に以下のような手法が用いられる。[1]

  • ヌルシーケンス‐無駄な命令の削除
  • 命令の結合‐複数の命令を等価なひとつの命令に置換
  • 代数法則の適用‐代数法則を用いることによる命令の簡略化または並べ替え
  • 特殊ケース命令‐特殊ケースのために設計された命令の使用
  • アドレスモード操作‐コードの簡略化のためのアドレスモードの使用

この他にも様々なのぞき穴的最適化が存在する。

遅い命令を速いものに置換

...
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

実装

現代的アーキテクチャではさまざまなのぞき穴的最適化が可能であり、コンパイラプログラマーパターンマッチングアルゴリズムを用いてこれを実装するのが基本的に適切である。[2]

関連項目

参考文献

  1. ^ Fischer, Charles N; Cytron, Ron K; LeBlanc, Richard J, Jr. (2010). Crafting a Compiler. Addison-Wesley. ISBN 978-0-13-606705-4. http://bank.engzenon.com/download/560e7301-482c-43fd-9f80-16a9c0feb99b/Crafting_a_Compiler_by_Fischer_Cytron_and_LeBlanc.pdf 2 July 2018閲覧。 
  2. ^ Aho, Alfred V; Lam, Monica S; Sethi, Ravi; Ullman, Jeffrey D (2007). “"8.9.2 Code Generation by Tiling an Input Tree"” (PDF). Compilers - Principles, Techniques, & Tools (second edition). Pearson Education. p. 540. オリジナルの10 June 2018時点におけるアーカイブ。. http://www.informatik.uni-bremen.de/agbkb/lehre/ccfl/Material/ALSUdragonbook.pdf 2 July 2018閲覧。 

外部リンク