2012-01-12 14 views
13

メモリを節約できるように、配列の配列ではなく多次元の配列を作成するコードを書きました。それから、いくつかのテストを実行して、通常のJava配列配列(int [] [])と速度を比較しました。メモリを節約してもプログラムの実行速度を遅くしたくないからです。私がタイミングテストで見たものは、私が混乱しています。テスト実行の典型的な結果を以下に示します。時は同じコードのものです。最後の2つが最初の4つよりもはるかに大きいことに注目してください。私は理解できないJavaのパフォーマンスの問題があります

時間:58343722 NS
時間:59451156 NS
時間:51374777 NS

時間:61777424 NS
時間:813156695 NS
時間:今782140511のNS

私が最初に思ったのは、ガベージコレクタが何を蹴っているのかということでした。メモリ制限を5GB(-Xmx5g)に上げて、ガベージコレクタが挑戦的に始まらない。何も変わっていません。私は物事を動かしましたが、パターンはそのままです。

パターンとは何ですか?最初の3回では、コードの一部は関数内にあり、私はそれを3回呼び出します。 2番目の3回では、コードのビットが1回の関数で3回繰り返されます。コードのビットが同じ関数で複数回実行されるたびに、コードのビットを実行するのにかかる時間は、コードの2番目のビットから始まり、そこにとどまるというパターンです。

私はこのような結果を生む1つの改変を見つけた:

時間:58729424 NS
時間:59965426 NS
時間:51441618 NS

時間:57359741のNS
時間: 65362705 ns
時間:857942387 ns

私がしたことは、2番目の3つのコードビットの間に1ミリ秒の遅延を追加することでした。これを行うと、ブロック内のコードビットの秒が高速化され、遅れてもコードビットの速度が上がることはありません。

率直に言って、私は混乱しています。私はこの行動を説明することはできません。誰かが何が起こっているかについていくつかの光を当てることができますか?

package multidimensionalarraytests; 

import java.lang.reflect.Array; 
import java.util.logging.Level; 
import java.util.logging.Logger; 

public class MultidimensionalArrayTests { 
    static ArrayInt2Dv1 array=new ArrayInt2Dv1(10000,10000); 

    public static void main(String[] args) { 
     System.out.println("ignore the warmup"); 
     test(); 
     test(); 
     combined(); 
     combined(); 

     System.out.println("running tests"); 
     test(); 
     test(); 
     test(); 
     System.out.println(); 
     combined(); 
    } 

    static long test(){ 
     int value=1; 
     long start,stop,time; 

     System.out.print("time: "); 
     start=System.nanoTime(); 
     for(int x=0;x<array.length1;x++){ 
      for(int y=0;y<array.length2;y++){ 
       array.set(x, y, value); 
       value=array.get(x, y); 
      } 
     } 
     stop=System.nanoTime(); 
     time=(stop-start); 
     System.out.println(time+" ns"); 
     return time; 
    } 

    static void combined(){ 
     int value=1; 
     long start,stop,time; 

     System.out.print("time: "); 
     start=System.nanoTime(); 
     for(int x=0;x<array.length1;x++){ 
      for(int y=0;y<array.length2;y++){ 
       array.set(x, y, value); 
       value=array.get(x, y); 
      } 
     } 
     stop=System.nanoTime(); 
     time=(stop-start); 
     System.out.println(time+" ns"); 

     //try {Thread.sleep(1);} catch (InterruptedException ex) {} 

     System.out.print("time: "); 
     start=System.nanoTime(); 
     for(int x=0;x<array.length1;x++){ 
      for(int y=0;y<array.length2;y++){ 
       array.set(x, y, value); 
       value=array.get(x, y); 
      } 
     } 
     stop=System.nanoTime(); 
     time=(stop-start); 
     System.out.println(time+" ns"); 

     //try {Thread.sleep(60000);} catch (InterruptedException ex) {} 

     System.out.print("time: "); 
     start=System.nanoTime(); 
     for(int x=0;x<array.length1;x++){ 
      for(int y=0;y<array.length2;y++){ 
       array.set(x, y, value); 
       value=array.get(x, y); 
      } 
     } 
     stop=System.nanoTime(); 
     time=(stop-start); 
     System.out.println(time+" ns");  
    } 
} 

と::オプション-Xms5g -Xmx5g -XXれたWindows 7上の

package multidimensionalarraytests; 

public class ArrayInt2Dv1 { 
    int [] array; 

    public final int length1; 
    public final int length2; 

    public ArrayInt2Dv1(int length1, int length2){ 
     this.length1=length1; 
     this.length2=length2; 
     array=new int[length1*length2]; 
    } 

    public int get(int x,int y){ 
     return array[x*length2+y]; 
    } 

    public void set(int x,int y,int value){ 
     array[x*length2+y]=value; 
    } 
} 

---編集---

出力ここ

はコードです: + PrintCompilation -verbose:gc -XX:CICompilerCount = 1 -Xbatch

time:  299 1 b  multidimensionalarraytests.ArrayInt2Dv1::set (15 bytes) 
    302 2 b  multidimensionalarraytests.ArrayInt2Dv1::get (14 bytes) 
    303 1 % b  multidimensionalarraytests.MultidimensionalArrayTests::test @ 31 (114 bytes) 
    358 1 %   multidimensionalarraytests.MultidimensionalArrayTests::test @ -2 (114 bytes) made not entrant 
60671451 ns 
    359 3 b  multidimensionalarraytests.MultidimensionalArrayTests::test (114 bytes) 
time:  365 2 % b  multidimensionalarraytests.MultidimensionalArrayTests::test @ 31 (114 bytes) 
58104484 ns 
time:  425 3 % b  multidimensionalarraytests.MultidimensionalArrayTests::combined @ 31 (330 bytes) 
69008251 ns 
time: 806898159 ns 
time: 845447596 ns 
    2146 4 b  multidimensionalarraytests.MultidimensionalArrayTests::combined (330 bytes) 
time: 52493169 ns 
time: 804304528 ns 
time: 845500191 ns 
running tests 
time: 51290771 ns 
time: 51922285 ns 
time: 51264108 ns 

time: 52258679 ns 
time: 842229025 ns 
time: 871403625 ns 
同じオプションを持つLinuxの(同じマシン上のVirtualBox上のUbuntu)で10

283 1 b java.lang.String::hashCode (60 bytes) 
    285 2 b sun.nio.cs.UTF_8$Encoder::encodeArrayLoop (490 bytes) 
    287 3 b java.lang.String::charAt (33 bytes) 
    287 4 b java.lang.String::indexOf (151 bytes) 
    297 5 b java.io.UnixFileSystem::normalize (75 bytes) 
    2850 6 b java.lang.String::lastIndexOf (156 bytes) 
ignore the warmup 
time: 5885 7 b multidimensionalarraytests.ArrayInt2Dv1::set (15 bytes) 
    5948 8 b multidimensionalarraytests.ArrayInt2Dv1::get (14 bytes) 
    5949 1% b multidimensionalarraytests.MultidimensionalArrayTests::test @ 31 (114 bytes) 
11529998483 ns 
    17565 9 b multidimensionalarraytests.MultidimensionalArrayTests::test (114 bytes) 
time: 1619622928 ns 
time: 19718 2% b multidimensionalarraytests.MultidimensionalArrayTests::combined @ 31 (330 bytes) 
475786382 ns 
time: 288586857 ns 
time: 315560700 ns 
    20789 10 b multidimensionalarraytests.MultidimensionalArrayTests::combined (330 bytes) 
time: 460577230 ns 
time: 311525066 ns 
time: 312343429 ns 
running tests 
time: 310261854 ns 
time: 298826592 ns 
time: 304689920 ns 

time: 315416579 ns 
time: 299473245 ns 
time: 290211350 ns 
+4

まずは、http://stackoverflow.com/questions/504103/how-do-i-write-a-correct-micro-benchmark-in-java – NPE

+0

をご覧ください。上記のコードを変更して推奨事項を反映させ、-Xms5g -Xmx5g -XX:+ PrintCompilation -verbose:gc -XX:CICompilerCount = 1 -Xbatchというオプションでプログラムを実行しました。しかし、それは有意に結果を変えなかった。 – user1145922

+1

私は(更新されたコードで)試しましたが、Java 6u26を64ビットUbuntuで使用してエフェクトを再現することはできません。ここでは、6つの経過時間はお互いに10%以内です(ウォーミングアップ時間はジャストインタイムコンパイラが蹴り込むため変動します)。 – NPE

答えて

6

は、最初のループが10000回を反復した後、全体の方法が最適化されていることを示す必要があります。この-XX:+PrintCompilationを試してみてください。問題は、統計/カウンタ情報なしで2番目または3番目のループが最適化されていることです。場合によってはこれは問題ではない場合があります。後のループは非常に遅く、ループの順序を入れ替えると、後のループが改善され、最初のループが遅くなります。

これを修正する簡単な方法は、各ループを独自の方法で配置し、各ループを適切に最適化することです。

+0

OK、あなたの話しが分かります。 combine()関数では、ループの初期集合の間にコンパイル演算が行われますが、他の演算は行われません。 1msを追加すると、コンパイル操作は最初の2つのループセットで発生しますが、3番目のループでは発生しません。コンパイル操作が実行されるループは、実行の高速化に対応します。それは、ループの最初のセットだけを最適化し、他のループは最適化しないことは、私にとってはむしろ奇妙なようです。まあ、少なくとも私はそれが何をしているのか分からない理由は分かっています。 – user1145922

+0

しかし、奇妙なことに、私はArrayInt2Dv1ではなくint [] []を使用すると同じ最適化が行われますが、最後の2つのループで最適化を実行しないために時間がかかることはありません。オプティマイザの仕組みを念頭に置かずに、配列のJava配列を多次元配列に置き換えることができないか、物事がかなり遅くなる可能性があるので、むしろ迷惑です。 – user1145922

+0

すべてのループは最適化されていますが、後のループは動的に収集された情報に基づいて最適化されています(メソッドが一度にコンパイルされるため、 –

関連する問題