2011-07-22 22 views
1

SunのJDKで提供されているJavaコンパイラが「スマート」であることについて、簡単な質問があります。具体的には、ループの繰り返しごとに評価するのではなく、for()ループの条件付き部分に現れる関数を評価するだけで十分ですか?Javaコントロール構造の自動コンパイラ最適化?

たとえば、次のコードを考えます。

// Array of doubles to "hold" the array. 
private double matrix[][]; 

public int getCols() { 
    // Compute the number of columns in a matrix. 
} 

public int getRows() { 
    // Compute the number of rows in a matrix. 
} 

// Compute the sum of all elements in the matrix. 
public double sum() { 

    double result = 0; 

    for (int r = 0; r < getRows(); r++) { 
     for (int c = 0; c < getCols(); c++) { 
      result += this.matrix[r][c]; 
     } 
    } 

    return result; 
} 

明らかに、I()は

public double sum() { 

    double result = 0; 
    int numRows = getRows(); 
    int numCols = getCols(); 

    for (int r = 0; r < numRows; r++) { 
     for (int c = 0; c < numCols; c++) { 
      result += this.matrix[r][c]; 
     } 
    } 

    return result; 
} 

にそれを変更することにより、ループの各反復で評価されていないの和()メソッドはそのGETROWS()とgetColsを確実に修正することができしかし、コンパイラがこれらを事前評価するのに十分なほどスマートであれば、私は不思議です。つまり、各繰り返しで評価するのではなく、事前に条件に表示されている関数を評価するのが計算上安価であることが自動的に検出されますか?

ありがとうございます!

+0

状態の依存関係が極端に複雑になるとは思っても、配列の長さプロパティを使用していないのはなぜですか?現在の設定は、すべての行が同じ数の列を持つことを暗黙的に前提としているため、少し脆いです。 – Taylor

答えて

5

一般に、このような最適化は、が間違ってになります。メソッドはバーチャルなので、サブクラスでは全く異なるもの(乱数を返すなど)を行うように変更することができ、静的に証明することは難しいかもしれません(証明する必要があります)たとえそれがfinalだったとしても、すべての反復で値を返します。実際には、まったくそうしないかもしれません(別のスレッドが行/列の数を並行して変更すると思うかもしれません - その場合、他の問題もたくさんありますが、それでも考慮する必要があります)。

それ以外は、コンパイラは何も最適化する必要はありません。実行時に、JITコンパイラはさらに最適化できます。例えば、仮想呼び出しをインラインにしてコードを生成することができ、コードに応じて(少なくとも無条件に定数を返す場合は)間違ったものを取り除くことができます。しかし、セマンティクスを変更できない場合は、これもやりません。それが本当に重要な場合は、それを自分で行います。いずれにしても、ボトルネックかどうかを確認してください。

+0

+1は非常に良い点です。私はちょうど指摘したい、これはいわゆる "最適化"は、Javaだけでなく、多くの言語で間違っているだろう。基本的に副作用を許す言語。 – Perception

+0

@delnanありがとうございました。いくつかの理由を指摘すると、一般的にコンパイラが期待することは妥当ではありませんでした。 – pmcs

2

私はコンパイラがとてもスマートになるとは思わない。メソッドgetRows()がどのように実装されているかはわかりません。あなたがそれを呼び出すたびに異なる値を返したらどうなるでしょうか?

だから、最終行。コードの2番目のバージョンがはるかに優れています。パフォーマンスの観点からだけでなく、それはより読みやすく、私はあなたがコーディングするときにこの方法を好むべきだと思います。

自分自身がforループに書き込むことを許可する唯一のメソッド呼び出しは、length()配列の呼び出しです。 Arrayは「本当の」クラスではなく、length()メソッドは実際のメソッドではないため、呼び出しによってパフォーマンスに影響はありません。

+0

+1:私は同意しますが、それは属性ではありません - 神だけが理由を知っています:-) – home

0

コンパイラがこれらの式を一度だけ評価するのはかなり恐ろしいでしょう。なぜなら、複数回評価される状況があるかもしれないからです。 しかし、あなたは少しテストを書くことができます。

public class Test { 

    private static int number = 5; 

    public static void main(String[] args) { 
     for(int i = 0; i < end(); i++) { 
      System.out.println(i); 
     } 
    } 

    public static int end() { 
     return number--; 
    } 

} 

出力:

0 
1 
2 

これは、あなたは、コンパイラが表現するたびに評価することを示しています。

0

コンパイラは最適化の横にはありませんが、Sun/Oracleコンパイラは2つの「仮想」メソッドをインライン化するほどスマートです。ゲッタの複雑さにもよりますが、違いはありません。私はそれが一つに二つのループを実行して、ループのコードをアンロール見てきました

for (int r = 0, rmax=getRows(), cmax=getCols(); r < rmax; r++) { 
    double[] cs = this.matrix[r] 
    for (int c = 0; c < cmax-1; c+=2) { 
     result += cs[c] + cs[c+1]; 
    } 
    if (cmax % 2 == 1) 
     result += cs[cmax-1]; 
} 

として

あなたのループは同じように十分にスマートでなければなりません。

あなたはOpenJDKののデバッグバージョンをダウンロードして、-XXを使用することに興味を賭けかもしれません:+ PrintAssembly http://wikis.sun.com/display/HotSpotInternals/PrintAssembly

+1

ありがとう! -XXを使用して生成されたコードを調べるヒントは、非常に便利です。 – pmcs

1

javacは、このような事を最適化しません。 JVMはそうです。

あなたのgetRows()getCols()の内容によって異なります。それらが非常にシンプルであれば、JVMによってインライン化されることはほぼ確実です。値が変更されないと結論づけることができれば、JVMはさらに最適化し、一度呼び出すと結果をキャッチすることができます。

たとえば、次のコード

for(int i=0; i<string.length(); i++) 
    char c = string.charAt(i); 

が積極的にJVMによって最適化されますが、当社による手動の最適化は、それを打つことができませんでした。

関連する問題