3

私はメソッドのパフォーマンスを調査しており、最終的にオーバーヘッドが if else ステートメントの「else」部分によって引き起こされていることを特定しました。コードの else 部分が実行されない場合でも、パフォーマンスの違いを示す小さなプログラムを作成しました。

public class TestIfPerf
{
    public static void main( String[] args )
    {   
        boolean condition = true; 
        long time = 0L;
        int value = 0;
        // warm up test 
        for( int count=0; count<10000000; count++ )
        {       
            if ( condition ) 
            {           
                value = 1 + 2;  
            }           
            else        
            {           
                value = 1 + 3;  
            }           
        }       
        // benchmark if condition only
        time = System.nanoTime();
        for( int count=0; count<10000000; count++ )
        {
            if ( condition )
            {
                value = 1 + 2;
            }           
        }
        time = System.nanoTime() - time; 
        System.out.println( "1) performance " + time ); 
        time = System.nanoTime();
        // benchmark if else condition
        for( int count=0; count<10000000; count++ )
        {
            if ( condition )
            {
                value = 1 + 2;
            }           
            else
            {
                value = 1 + 3;
            }
        }
        time = System.nanoTime() - time; 
        System.out.println( "2) performance " + time ); 
    }   
}

でテストプログラムを実行しjava -classpath . -Dmx=800m -Dms=800m TestIfPerfます。

1.6 の最新ビルドを使用して、Mac と Linux Java の両方でこれを実行しました。一貫して、条件のためにelse部分が決して実行されないようにコードが構造化されているにもかかわらず、elseセクションがない最初のベンチマークは、elseセクションのある2番目のベンチマークよりもはるかに高速です。一部の人にとって、違いは重要ではないかもしれませんが、相対的なパフォーマンスの違いは大きいことを理解しています. 誰かがこれについて何か洞察を持っているのだろうか(または、私が間違ったことをしたのかもしれません)。


Linux ベンチマーク (nano)

  1. パフォーマンス 1215488
  2. パフォーマンス 2629531

Mac ベンチマーク (nano)

  1. パフォーマンス 1667000
  2. パフォーマンス 4208000
4

6 に答える 6

6

あなたのテストは二段ベッドです。テスト条件を入れ替えると、正反対の結果が得られます。

1) performance 5891113
2) performance 15216601

2) performance 5428062
1) performance 15087676

おそらく、実行が進むにつれてコードを最適化するJVMに関係しています。条件を数回コピーして貼り付けると、次のようになります。

2) performance 6258694
1) performance 34484277
2) performance 978
1) performance 978
2) performance 908
于 2012-12-15T23:14:29.360 に答える
2

考えられる説明は 2 つあります。

  • あなたが得ている時間は、ベンチマークの欠陥によって歪められています. あなたは多くのことを間違っています - How do I write a correct micro-benchmark in Java? を参照してください。

  • 純粋なバージョンは、elseループの反復ごとに少し時間がかかります。その場合、いくつかの可能な説明があります。これを理解する最善の方法は、JIT コンパイラーによって生成されたネイティブ コードを見て、そのパフォーマンスを分析することです。

しかし、肝心なのは、これは驚くべきことではなく (上記を参照)、大多数の Java アプリケーションにとって実際の結果ではないということです。「if then」または「if then else」が必要かどうかを判断するのはアプリケーションです。

そして、このような人為的なマイクロ ベンチマークから得られる情報が、実際のコードに役立つかどうかは疑わしいものです。JIT コンパイラーは、テストが実行しているよりも高度なレベルでコードを最適化する可能性があります。ここに表示される内容 (ベンチマークに問題がなければ) は、実際のアプリケーションに反映される可能性は低いです。

于 2012-12-15T23:00:31.187 に答える
0

Java コード:

        for (int count = 0; count < 10000000; count++) {
            // start of the if
            if (cond) {
                value = 1 + 2;
            }
            // end of the if
        }

Java バイトコード:

   7:   lstore_3      //store a long value in a local variable 3
   8:   iconst_0      //load the int value 0 onto the stack
   9:   istore  5     //store int value into variable #index
   11:  goto    23    //goes to another instruction at branchoffset
   14:  iload_1       //load an int value from local variable 1
   15:  ifeq    20    //if value is 0, branch to instruction at branchoffset
   18:  iconst_3      //load the int value 3 onto the stack
   19:  istore_2      //store int value into variable 2
   20:  iinc    5, 1  //increment local variable #index by signed byte const
   23:  iload   5     //load an int value from a local variable #index
   25:  ldc #22;  //push a constant #index from a constant pool (String, int or float) onto the stack - int 10000000
   27:  if_icmplt 14  //if value1 is less than value2, branch to instruction at branchoffset

Java コード:

        for (int count = 0; count < 10000000; count++) {
            // start of the if
            if (cond) {
                value = 1 + 2;
            } else {
                value = 1 + 3;
            }
            // end of the if
        }

Java バイトコード:

   63:  lstore_3      //store a long value in a local variable 3
   64:  iconst_0      //load the int value 0 onto the stack
   65:  istore  5     //store int value into variable #index
   67:  goto    84    //goes to another instruction at branchoffset
   70:  iload_1       //load an int value from local variable 1
   71:  ifeq    79    //if value is 0, branch to instruction at branchoffset
   74:  iconst_3      //load the int value 3 onto the stack
   75:  istore_2      //store int value into variable 2
   76:  goto    81    //goes to another instruction at branchoffset
   79:  iconst_4      //load the int value 4 onto the stack
   80:  istore_2      //store int value into variable 2
   81:  iinc    5, 1  //increment local variable #index by signed byte const
   84:  iload   5     //load an int value from a local variable #index
   86:  ldc #22;  //push a constant #index from a constant pool (String, int or float) onto the stack - int 10000000
   88:  if_icmplt 70  //if value1 is less than value2, branch to instruction at branchoffset
于 2012-12-16T09:17:41.427 に答える
0

2 番目の測定で else ブランチをコメント アウトすると、さらに奇妙になります。現在は同じコードですが、それでも遅くなります。幸いなことに、このコードを別の方法で抽出すると、2 回目ははるかに高速に実行されます。

私が考えることができる唯一のことは、JVM が長いメソッドの最初の部分だけを最適化するということです。ええ: if-else 測定を最初に置くと、より高速になります。

于 2012-12-15T23:03:12.723 に答える
0

VM の初期化 (ウォームアップが非常に短い) または時間測定のジッター (VM の起動に関連) に関連している必要があります。

ループを交換すると、ループ 2 が速くなります :-)

一般に、ホットスポット JIT は適切ですが、信頼性が低く、決定論的ではありません。Java で最高のパフォーマンスを得るには

  • オブジェクトの作成を避ける
  • vars に可能な限り final のフラグを立てます。これは違いがない場合もありますが、違いがある場合もあります。ループ内でインスタンス変数にアクセスするときは、それらを最終的なローカルにコピーします。
  • 可能であれば、メソッドを final としてマークします。
  • メソッドをインライン化したい場合は、短くして、分割してください。これをテストで何度も確認しました。「void foo() { if (..) return ..stuff... }」を分割すると、「foo() { if (..) return; else foo1() } void foo1() { スタッフ }"

一般に、マイクロ ベンチマークを使用してパフォーマンス パターンを証明することは非常に困難です。なぜなら、何がインライン化、jit コンパイル、およびさらなるランタイム最適化をトリガーしているのか正確にはわからないからです。JIT にはしきい値があるため、メソッドにステートメントを追加したり、既存のクラスのサブクラスを追加しただけで、パフォーマンスが低下することがあります。

于 2012-12-15T23:24:01.570 に答える
-1
public class Main {

public static void main(String[] args) {
    boolean cond = true;
    int nothing = 0;

    for (int i = 0; i < 20; i++) {
        int value = 0;
        long time = System.nanoTime();
        for (int count = 0; count < 10000000; count++) {
            if (cond) {
                value = 1 + 2;
            }
        }

        time = System.nanoTime() - time;
        System.out.println("1) performance: " + time);
        nothing = value; // prevent java ignoring value

        value = 0;
        time = System.nanoTime();
        for (int count = 0; count < 10000000; count++) {
            if (cond) {
                value = 1 + 2;
            } else {
                value = 1 + 3;
            }
        }
        time = System.nanoTime() - time;
        System.out.println("2) performance: " + time);
        nothing = value; // prevent java ignoring value
    }

    nothing = nothing + 1;
}
}

結果は次のとおりです。

1) performance: 1797000
2) performance: 3742000
1) performance: 7290000
2) performance: 0
1) performance: 0
2) performance: 0
1) performance: 0
2) performance: 0
1) performance: 0
2) performance: 0
1) performance: 0
2) performance: 0
1) performance: 1000
2) performance: 0
1) performance: 0
2) performance: 0
1) performance: 0
2) performance: 0
1) performance: 0
2) performance: 0
1) performance: 0
2) performance: 0
1) performance: 0
2) performance: 0
1) performance: 1000
2) performance: 0
1) performance: 0
2) performance: 0
1) performance: 0
2) performance: 0
1) performance: 0
2) performance: 0
1) performance: 0
2) performance: 0
1) performance: 0
2) performance: 0
1) performance: 0
2) performance: 0
1) performance: 0
2) performance: 0
于 2012-12-16T01:02:01.453 に答える