コンテンツにスキップ

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

デッドコード削除

出典: フリー百科事典『ウィキペディア(Wikipedia)』

デッドコード削除またはデッドコード除去: dead code elimination)は、コンピュータ・プログラムの最適化などの目的で行われるプログラムの変形のひとつで、到達不能コードや冗長なコードなどといった「デッドコード」を削除する操作である。効果としては、最適化として見た場合、コードサイズの削減(いわゆるフットプリントの縮小)や、それに伴うキャッシュの利用効率の向上などによる高速化も期待できるかもしれない。

[編集]

次のC言語のコード例を見てみよう。

int foo() {
  int a = 24;
  int b = 25; /* 参照されない変数への代入 */
  int c;
  c = a << 2;
  return c;
  b = 24; /* 実行されないコード */
}

変数bにはreturn文の後に値が代入されているが、これは実行されない。すなわち、コードの実行は逐次的であり、return文が何らかの条件で囲まれているわけでもないので、return文の後のコードは実行不可能(到達不可能)である。ただし、例えばreturn文の後にラベルがあって、どこかから分岐して飛んでくる場合は、その限りではない。

さらに、この代入文を削除すると、bという変数が初期値を代入したとき以外では全く使われないことがわかる。オプティマイザがどこまで積極的に最適化するかにもよるが、変数bは生成されるコードから完全に削除される可能性もある。

関数内で何らかの計算が行われたとしても、その結果がこの関数のスコープ外からアクセス可能な位置に格納されなければ、その計算は無意味である。さらにこの関数は常に同じ値 (96) を返すので、単にその値を返す関数として単純化される可能性もある。

最近[いつ?]コンパイラはデッドコード削除を行うか否かをオプションで指定でき、場合によってはそのレベルを指定できるものもある。低いレベルでは、実行されないコードのみを削除するだろう。もう少し高いレベルでは、使われない変数のための領域を確保しないことになる。さらに高いレベルでは、意味のないコードや関数を特定して、それらを削除する。

デッドコード削除の典型的な利用例として、プリプロセッサによるオプションコードの代替としての使い方がある。次のC言語で書かれたコードを見てみよう。

/* デバッグ時には 1 とする */
#define ENABLE_DEBUG_PRINT 0

int main() {
  int a = 5;
  int b = 6;
  int c;
  c = a * (b >> 1);
  if (ENABLE_DEBUG_PRINT) {
    printf("%d\n", c);
  }
  return c;
}

ENABLE_DEBUG_PRINTはプリプロセッサによって0に展開される。if文の条件式が0ということは、常に偽であることを意味し、このif文の中のコードは実行されない。そのため、コンパイラの最適化により、デッドコードとして除去される可能性が高い。デバッグ用のコードを残しておく際に使われることのあるテクニックである。

しかし、以下のようなコードでは、たとえif文の条件式が常に偽であっても、if文内部のコードは到達不能ではなく、実行されることがあるため、最適化による除去の対象とはならない。したがって、コンパイラは単純にif文の条件式だけをもとに一律デッドコードか否かを判断することはできず、if文内部のパース(解析)がやはり必要である。

  if (0) {
label_inner:
    puts("Inner");
    goto label_exit;
  }
  goto label_inner;
label_exit:
  puts("Exit");

もしリリース時に静的にコードを除去する目的があるのであれば、最適化によるデッドコードの除去を期待するよりも、プリプロセッサを用いるほうが確実であり、ひいては総合的なコンパイル時間およびビルド時間を短縮することにもつながる。また、到達不能コードのあるプログラムにはバグが潜んでいる可能性が高く、通例コンパイラが警告を発する。警告を無視することは、バグを無視することにつながる危険性もある。

リフレクション

[編集]

たとえ構文解析上はどこからも参照されない関数メソッドや変数/定数/フィールドプロパティやデータ型であったとしても、リフレクション機能をサポートする言語の場合、冗長コードとみなして削除してしまうのは問題がある[1]ダックタイピングなどの目的で、実行時に(動的に)文字列とリフレクションAPIを利用してメソッドやフィールドやクラスなどにアクセスする場合、コンパイル時の静的な構文解析で認識可能な通常の識別子経由でそれらが使われていないからといって、それらを完全に削除してしまうとリフレクションが正常に動作しなくなるからである。この制限はコードの難読化の際にもあてはまり、勝手に識別子を変更・短縮してしまうとリフレクションが動作しなくなることがある。

脚注

[編集]

関連項目

[編集]