2012-03-05 14 views
2

特定の数のスレッドで特定の数のコインをフリップするクラスの単純なコインフリッピング実験を行っています。スピードアップのために私たちのパフォーマンステストを実行するために、固定数のコインフリップ(私は10億を使用しています)を使用してスレッド数を変更します。これらのテストを実行するには、8コアのAWS余分な高CPUインスタンスを使用します。何らかの理由で、6つ以上のスレッドを使用するとすぐに、私は大幅に減速します。それよりも悪い、それは矛盾している。時々私は14秒、時には同じ数のスレッドとフリップで2になるでしょう。意味がない。私は別のJVM(OpenJREとSun JVM)を使い、新しいインスタンスを試してみました。以下は私のコードとベンチマーク結果(ミリ秒)です。私はいくつかの助けが大好きです。ありがとう。6つ以上のスレッドを使用するとパフォーマンスが大幅に低下する

EDIT:それで、私はそれを解決したようです.YadabとBruno Reisの提案に大いに感謝します。彼らは、ローカル変数を使用して頭の数を追跡することを提案しました。これは要因だったと思います。彼らはまた、同じJVMセッション内からすべてのテストを実行することを提案しました。これはほぼ間違いなく要因でした。みんなあなたの助けてくれてありがとう。

Speedup: 
Threads | Flips | Time 
1  1000000000 16402 16399 16404 
2  1000000000 8218 8216 8217 
3  1000000000 5493 5483 5492 
4  1000000000 4125 4127 4140 
5  1000000000 3306 3304 3311 
6  1000000000 2758 2766 2756 
7  1000000000 8346 7874 10617 
8  1000000000 14370 14414 17831 
9  1000000000 14956 14764 15316 
10  1000000000 13595 14491 14031 
11  1000000000 12642 11188 10625 
12  1000000000 10620 10629 10876 
13  1000000000 8422 9950 9756 
14  1000000000 9284 9546 10194 
15  1000000000 8524 4134 8046 
16  1000000000 6915 6361 7275 

コード:

import java.util.Random; 

public class CoinFlip implements Runnable { 
    private final long iterations; //iterations is the number of times the program will run, numHeads is the number of heads counted 
    private long numHeads; 
    public CoinFlip(long iterations) { 
     this.iterations = iterations; 
    } 

    @Override 
    public void run() { 
     Random rand = new Random(); 
     numHeads = 0; 
     for (long i = 0; i < iterations; i++) { 
      if (rand.nextBoolean()) { //True represents heads, false represents a tails 
       numHeads++; 
      } 
     } 
    } 

    public long getHeads() { //numHeads getter 
     return numHeads; 
    } 

    public static void main(String[] args) { 
     final long numIterations , itersPerThread; //iterations: number of iterations, threads: number of threads to run on, itersPerThread: how many iterations each thread is responsible for 
     final int threads; 
     if (args.length != 2) { 
      System.out.println("Usage: java CoinFlip #threads #iterations"); 
      return; 
     } 
     try { 
      threads = Integer.parseInt(args[0]); 
      numIterations = Long.parseLong(args[1]); 
     } catch (NumberFormatException e) { 
      System.out.println("Usage: java CoinFlip #threads #iterations"); 
      System.out.println("Invalid arguments"); 
      return; 
     } 
     itersPerThread = numIterations/((long)threads); //Might cause rounding errors, but we were told to ignore that 
     Thread[] threadList = new Thread[threads]; //List of running threads so we can join() them later 
     CoinFlip[] flipList = new CoinFlip[threads]; //List of our runnables so that we can collect the number of heads later 
     for (int i = 0; i < threads; i++) { //create each runnable 
      flipList[i] = new CoinFlip(itersPerThread); 
     } 
     long time = System.currentTimeMillis(); //start time 
     for (int i = 0; i < threads; i++) { //create and start each thread 
      threadList[i] = new Thread(flipList[i]); 
      threadList[i].start(); 
     } 
     for (int i = 0; i < threads; i++) { //wait for all threads to finish 
      try { 
       threadList[i].join(); 
       System.out.println("Collected thread " + i); 
      } catch (InterruptedException e) { 
       System.out.println("Interrupted"); 
       return; 
      } 
     } 
     time = System.currentTimeMillis() - time; //total running time 
     long totHeads = 0; 
     for (CoinFlip t : flipList) { //Collect number of heads from each CoinFlip object 
      totHeads += t.getHeads(); 
     } 

     //Print results 
     System.out.println(totHeads + " heads in " + (numIterations/threads) 
       * threads + " coin tosses on " + threads + " threads"); 
     System.out.println("Elapsed time: " + time + "ms"); 
    } 
} 
+1

テストシステムにはいくつのコアがありますか? – Tudor

+0

アプリケーションにどのくらいのメモリを設定しましたか(-Xmx)? –

+0

テストシステムに8つのコアがあります(そのように編集されています)。私は、メモリの量を設定していない、私はちょうど、デフォルトであったものを使用した。これはおそらく十分なはずです。なぜなら、メモリで何もしていないので、rand.nextBooleanという形での計算です。 –

答えて

2

短い時間(30秒未満)で実行されるJavaテストは、パフォーマンステストには適していません。ホットスポットコンパイラやその他のJavaランタイム機構は、アプリケーションが実行されている最初の長い時間(秒)にコードを最適化しています。タイミングの偏差は、JVMの起動、最適化、およびシャットダウンに起因する可能性があります。

より現実的なタイミングを望む場合は、30秒間実行しなければならず、を入力してからタイミングを開始します。また、OSのオーバーヘッド、GC、バックグラウンドタスクなどの影響を平均化するためには、テスト実行に少なくとも1桁の桁数を掛けることをお勧めします。30秒間実行するアプリケーションをウォームアップし、タイマーを停止し、結果を記録してから、JVMをシャットダウンします。

また、ある一定の時間内に何回コインフリップをしたのかをグラフ化して、ある程度のコインフリップを行うのにどれくらい時間がかかるかを知ることは理にかなっています。違いは、できればすべてのテストを同じ時間実行することです。

5

限り、あなたは唯一のCPUバウンドの操作を実行しているとして、利用可能なコアよりも多くのスレッドを使用してリッテ感があります。逆に、追加のスレッドを使用すると、コンテキストの切り替えやスケジューリングのオーバーヘッドが増加します。

+0

これらのマシンは8つのコアを持つはずなので、最大8つのスレッドでうまく動くはずです。パフォーマンスの低下が劇的にならないように感じます... –

+0

1)実行していますか「冷たい」JVMで?または、再起動せずにいくつかの実行サイクルの後でベンチマークを開始しましたか? 2)他のアクティブなプロセスがマシン上で実行されていますか? –

+0

PartlyCloudy:1)はい、試行ごとに、Java CoinFlipを使って新しいJVMを起動しました。それぞれのJVMはそれ自身で実行されるはずです。2)デフォルトのubuntuシステムがインストールされているものを除き、システム上で動作するものはありません。何も重い。 –

2

あなたのスレッドはCPUを大量に消費します。つまり、スレッドが遅いリソースを待つのを待たずにスレッドがCPUを競争するようにブロックしません。

私は、他のスレッドを実行するために、各スレッドが一時停止すると確信しています。つまり、実行時間スライスは常に使い果たされます。したがって、実際には6スレッド(6つのスレッドが同時に実行できると仮定した場合)と比較して、スレッド間で多くのコンテキストを切り替えることができます。

3

VMを実行している場合、使用可能なコアも仮想である可能性があります。場合によっては8つの異なるコアが得られることもあります。また、時には4つのコア×2つのスレッドが得られることもあります。

私は、基礎となるマシンはあなたがスレッドの数は、あなたが持っているCPUコアの数よりも大きくてはならない8.

0

まで使用できるの2つのスレッドそれぞれに6つのコアを持っている疑いがあります。VMがスレッドを切り替える必要があるため、ペナルティを受ける可能性があります。

1

ソースのすばやい部分には、ボトルネックが見られません(どこでも細かい同期はありません)。

コメントには、クラウドサービスで実行していることが記載されています。ほとんどの場合、他のクライアントにもサービスを実行する仮想化されたシステムです。 この仮定が当てはまる場合、システムが自分の作業負荷を脇で実行する可能性がある他の処理が分からないため、意味のあるベンチマークを実行することは期待できません。

ローカルワークステーションでテストを試してみると、ばらつきははるかに少なくなるはずですが、当然のことながらいくつかのバリエーションがあります(各スレッドに等しいCPUスライスが提供されるという保証はありません)。

関連する問題