5

finalそれが主な目的ではないとしても、キーワード (場合によっては VM の実装で) が JIT に役立つ可能性があると私は常に考えてきました。
都市伝説かもしれませんが、フィールドを設定するfinalことでパフォーマンスに悪影響を及ぼすとは思いもしませんでした。

そのようなコードに遭遇するまで:

   private static final int THRESHOLD = 10_000_000;
   private static int [] myArray = new int [THRESHOLD];

   public static void main(String... args) {
      final long begin = System.currentTimeMillis();

      //Playing with myArray
      int index1,index2;
      for(index1 = THRESHOLD - 1; index1 > 1; index1--)
          myArray[index1] = 42;             //Array initial data
      for(index1 = THRESHOLD - 1; index1 > 1; index1--) {
                                            //Filling the array
          for(index2 = index1 << 1; index2 < THRESHOLD; index2 += index1)
              myArray[index2] += 32;
      }

      long result = 0;
      for(index1 = THRESHOLD - 1; index1 > 1; index1-=100)
          result += myArray[index1];

      //Stop playing, let's see how long it took
      System.out.println(result);
      System.out.println((System.currentTimeMillis())-begin+"ms");
   }


見てみましょう: private static int [] myArray = new int [THRESHOLD];
W7 64 ビットで、10 回の連続実行に基づいて、次の結果が得られます。

  1. THRESHOLD = 10^7、1.7.0u09 クライアント VM (Oracle):

    • myArrayが最終ではない場合、約 2133 ミリ秒で実行されます。
    • myArrayが最終的な場合、〜 2287 ミリ秒で実行されます。
    • -server VM では、2131 ミリ秒と 2284 ミリ秒という同様の数値が生成されます。

  2. THRESHOLD = 3x10^7、1.7.0u09 クライアント VM (Oracle):

    • myArray最終ではない場合、〜 7647 ミリ秒で実行されます。
    • myArrayが最終的な場合、〜 8190 ミリ秒で実行されます。
    • -server VM は ~7653ms と ~8150ms を生成します。

  3. THRESHOLD = 3x10^7、1.7.0u01 クライアント VM (Oracle):

    • myArray最終ではない場合、約 8166 ミリ秒で実行されます。
    • myArrayが最終的なときは ~9694ms で実行されます。その差は15%以上!
    • -server VM は、非最終バージョンを支持する無視できる差異 (約 1%) を生成します。

注意: すべてのテストで、JDK 1.7.0u09 の javac によって生成されたバイトコードを使用しました。myArray生成されたバイトコードは、宣言を除いて、両方のバージョンでまったく同じです。

では、なぜ のバージョンは のバージョンstatic final myArrayよりも遅いのstatic myArrayでしょうか?


編集(私のスニペットのAubinのバージョンを使用):

finalキーワードを含むバージョンと含まないバージョンの違いは、最初の反復のみにあるようです。どういうわけか、final最初の反復では、 を使用したバージョンは常に対応するバージョンよりも遅くなり、次の反復では同様のタイミングになります。

たとえば、THRESHOLD = 10^81.7.0u09 クライアントで実行すると、最初の計算には約 35 秒かかりますが、2 番目の「のみ」には 30 秒かかります。

明らかに、VM は最適化を実行しました。それは JIT が動作していたのですが、なぜ以前に起動しなかったのですか (たとえば、ネストされたループの 2 番目のレベルをコンパイルすることによって、この部分がホットスポットでした)。

私の発言は 1.7.0u01 クライアント VM でも有効であることに注意してください。そのバージョン(およびおそらく以前のリリース) では、このキーワードを使用しないコードfinal myArrayよりも実行速度が遅くなります: 200 回の反復に基づいて 2671ms 対 2331ms です。

4

1 に答える 1

4

I/Oは非常に可変で時間がかかるため、 System.out.println( result ) の時間を追加しないでください。

最終的な影響よりも、println() の影響の方が大きく、本当に大きいと思います。

パフォーマンス テストを次のように書くことを提案します。

public class Perf {
   private static final int   THRESHOLD = 10_000_000;
   private static final int[] myArray   = new int[THRESHOLD];
   private static /* */ long  min = Integer.MAX_VALUE;
   private static /* */ long  max = 0L;
   private static /* */ long  sum = 0L;

   private static void perf( int iteration ) {
      final long begin = System.currentTimeMillis();

      int index1, index2;
      for( index1 = THRESHOLD - 1; index1 > 1; index1-- ) {
         myArray[ index1 ] = 42;
      }
      for( index1 = THRESHOLD - 1; index1 > 1; index1-- ) {
         for( index2 = index1 << 1; index2 < THRESHOLD; index2 += index1 ) {
            myArray[ index2 ] += 32;
         }
      }
      long result = 0;
      for( index1 = THRESHOLD - 1; index1 > 1; index1 -= 100 ) {
         result += myArray[ index1 ];
      }
      if( iteration > 0 ) {
         long delta = System.currentTimeMillis() - begin;
         sum += delta;
         min = Math.min(  min,  delta );
         max = Math.max(  max,  delta );
         System.out.println( iteration + ": " + result );
      }
   }

   public static void main( String[] args ) {
      for( int iteration = 0; iteration < 1000; ++iteration ) {
         perf( iteration );
      }
      long average = sum / 999;// the first is ignored
      System.out.println( "Min    : " + min     + " ms" );
      System.out.println( "Average: " + average + " ms" );
      System.out.println( "Max    : " + max     + " ms" );
   }
}

そして、10回の繰り返しの結果は次のとおりです。

最終的に:

Min    : 7645 ms
Average: 7659 ms
Max    : 7926 ms

最終なし:

Min    : 7629 ms
Average: 7780 ms
Max    : 7957 ms

読者がこのテストを実行し、結果を投稿して比較することをお勧めします。

于 2012-11-01T08:13:14.270 に答える