2012-02-15 19 views
0

私は、Javaの並行処理のパフォーマンスをテストするためのおもちゃプログラムを作った。私はここに置く: https://docs.google.com/open?id=0B4e6u_s5iHT6MTNkZGM5ODQtNjZmYi00NTMwLWJlMjUtYzViOWZlMDM5NGViスケーラビリティが消えた

これは、使用するスレッドの数を示す整数値を引数として受け取ります。このプログラムは、ある範囲の素数を計算します。一般的なバージョンは44行目から53行目のコメントで得られ、ほぼ完璧なスケーラビリティを生成します。

ただし、ローカルで単純な計算を行う44〜53行のコメントを外して、変数sを十分大きな値に調整すると、スケーラビリティが消えることがあります。

私のおもちゃプログラムが共有データを使用するかどうかは、並行性のパフォーマンスが低下する可能性があります。そして、消えたスケーラビリティを説明する方法(私はガベージコレクションのような低レベルのオーバーヘッドがそれを引き起こすと思います)?どのような解決策でもこのような問題を解決できますか?問題の

+0

はい、16コアCPUでテストしました。 –

+0

別の8コアコンピュータで同じ設定をテストしたところ、スケーラビリティが再び現れました。私はこのおもちゃプログラムを使用して、次回のどこかでJava並行処理を試してみると、最初のステップのテストを行います。ところで、プライマー番号を計算するより良い方法は、スレッドの範囲を分割するのではなく、スレッドが候補番号を保持する並行キューを共有できるようにすることです。このおもちゃプログラムで共有データを避けたいだけです。 –

答えて

2

コードは次のとおりです。もちろん

int s = 32343; 
ArrayList<Integer> al = new ArrayList<Integer>(s); 
for (int c = 0; c < s; c++) { 
    al.add(c); 
} 
Iterator<Integer> it = al.iterator(); 
if (it.hasNext()) { 
    int c = it.next(); 
    c = c++; 
} 

あなたがsの値を増やした場合、これはあなたがリストに入れてどのように多くの事s制御するので、パフォーマンスが低下します。しかし、これは並行性やスケーラビリティとはほとんど関係がありません。数千または数百万回の捨て去り計算を行う時間を無駄にするようにコンピュータに指示するコードを書くと、もちろんパフォーマンスは低下します。 nsに等しい、より技術的に言えば

、コードのこのセクションの時間複雑性はO(2n)である(それはリストを構築するためにn操作を要し、その後n操作はそれを反復し、それぞれの値をインクリメントします)。したがって、あなたがsを大きくするほど、このコードを実行するのに時間がかかります。

これが並行性の利点を小さくするように見える理由については、sが大きくなるにつれてメモリの影響を考慮していますか?たとえば、Javaヒープがディスクにスワップアウトされずにメモリ内のすべてを保持するのに十分な大きさであることを確認してください。何もスワップアウトされなくても、ArrayListの長さを大きくすることで、ガベージコレクタに実行時の処理(実行頻度を上げる可能性があります)が増えます。実装によっては、ガベージコレクタが実行されるたびにすべてのスレッドを一時停止している可能性があります。

スレッドごとに単一のArrayListインスタンスを割り当ててスレッドを作成した時点で、毎回新しいリストを作成するのではなくisPrime()の呼び出しでそれを再利用すれば、改善されますか?

編集:ここまでの修正版です:http://pastebin.com/6vR7Uhez

は、それは私のマシン上で次のような出力が得られます。

------------------start------------------ 
1 threads' runtimes: 
1  3766.0 
maximum: 3766.0 
main time: 3766.0 
------------------end------------------ 
------------------start------------------ 
2 threads' runtimes: 
1  897.0 
2  2483.0 
maximum: 2483.0 
main time: 2483.0 
------------------end------------------ 
------------------start------------------ 
4 threads' runtimes: 
1  576.0 
2  1473.0 
3  568.0 
4  1569.0 
maximum: 1569.0 
main time: 1569.0 
------------------end------------------ 
------------------start------------------ 
8 threads' runtimes: 
1  389.0 
2  965.0 
3  396.0 
4  956.0 
5  398.0 
6  976.0 
7  386.0 
8  933.0 
maximum: 976.0 
main time: 978.0 
------------------end------------------ 

...スレッド数を上げているとほぼリニアスケーリングを示していますアップ。私が修正した問題は、上に挙げた点とJohn Vint(今削除された)の答え、そしてConcurrentLinkedQueue構造の間違った/不必要な使用と疑わしいタイミングロジックの組み合わせだった。

我々はGCログを有効にして、両方のバージョンをプロファイルした場合、我々は元のバージョンが変更されたバージョンよりも、ガベージコレクションの実行についての10倍ほどの時間を費やしていることがわかります。

Original: [ParNew: 17401K->750K(19136K), 0.0040010 secs] 38915K->22264K(172188K), 0.0040227 secs] 
Modified: [ParNew: 17024K->0K(19136K), 0.0002879 secs] 28180K->11156K(83008K), 0.0003094 secs] 

間のように私には意味定数リスト割り当てとInteger自動ボクシングの場合、元の実装はあまりにも多くのオブジェクトを突っ込んでいたため、GCに負担がかかりすぎて、スレッドのパフォーマンスが悪くなったり、より多くのスレッドを作成します。

Javaの並行性のスケーリングがうまくいかない場合は、タスクが大小どちらであっても、どのようにメモリを使用しているのか、隠されている可能性があることに注意する必要があります落とし穴や非効率性を減らし、非効率なビットを最適化します。

+0

こんにちは@aroth、私の場合、 's'は並行性とスケーラビリティに非常に関係しています。このコードは各スレッドによって繰り返し実行されます。 –

+0

1スレッド設定のスレッドは、このコード部分を「n回」実行します。 2スレッド設定のすべてのスレッドは 'n/2 '回実行します。 ... –

+0

はい、私はその部分を取得します。問題は、コード自体が実際には何もしないということです。作成または変更するステートは、それ以外の場所では使用されません。だからコードには本当に何も関係ない*他のすべての速度を落とすことを除いて、それを実行するには観察可能な効果がないため。それは単に演算処理の無駄です。 – aroth

関連する問題