2012-02-17 10 views
4

私はn個のスレッドを開始し、各スレッドに負荷をかける簡単なプログラムを持っています。 私は1つのスレッドだけを開始すると、1つのコアが約100%の負荷を得ます。 1つのプロセスを16スレッド(コアごとに1つのスレッドを意味します)で開始すると、負荷が約80%になります。 2つのスレッド(コアごとに1つのスレッドを意味する)で8つのプロセスを開始すると、約99%の負荷がかかります。 このサンプルではロックを使用していません。なぜ2つのスレッドを持つ8つのプロセスが、16のスレッドを持つ1つのプロセスより多くの負荷を生成するのですか?

この動作の理由は何ですか? OSが多くのスケジュールを設定する必要があるため、100スレッドが動作すると負荷が低下することを理解しています。 しかしこの場合、コアと同じ数のスレッドしかありません。

さらに悪いです(私にとっては少なくとも)。 ループに単純なthread.sleep(0)を追加すると、1つのプロセスと16スレッドの負荷が最大95%増加します。

誰でもこれに答えたり、この特定のトピックに関する詳細な情報を提供することはできますか?

One Process 16 threads

Eight Process 2 threads

One Process 16 threads with thread.sleep(0)

//Sample application which reads the number of threads to be started from Console.ReadLine 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      Console.WriteLine("Enter the number of threads to be started"); 
      int numberOfThreadsToStart; 

      string input = Console.ReadLine(); 

      int.TryParse(input, out numberOfThreadsToStart); 
      if(numberOfThreadsToStart < 1) 
      { 
       Console.WriteLine("No valid number of threads entered. Exit now"); 
       Thread.Sleep(1500); 
       return; 
      } 

      List<Thread> threadList = new List<Thread>(); 
      Stopwatch sw = Stopwatch.StartNew(); 
      for (int i = 0; i < numberOfThreadsToStart; i++) 
      { 
       Thread workerThread = new Thread(MakeSomeLoad); 
       workerThread.Start(); 
       threadList.Add(workerThread); 
      } 

      while (true) 
      { 
       Console.WriteLine("I'm spinning... "); 
       Thread.Sleep(2000); 
      } 
     } 

     static void MakeSomeLoad() 
     { 
      for (int i = 0; i < 100000000; i++) 
      { 

       for (int j = 0; j < i; j++) 
       { 
        //uncomment the following line to increase the load 
        //Thread.Sleep(0); 
        StringBuilder sb = new StringBuilder(); 
        sb.Append("hello world" + j); 
       } 
      } 
     } 
    } 

答えて

6

あなたのテストは非常に重いGCを探します。 1つのプロセスに16個のスレッドがある場合、GCはそのプロセスでさらに実行され、クライアントGCは並列ではないため、負荷が低くなります。つまり、GCスレッドごとに16個のガベージ生成スレッドがあります。

一方、それぞれ2つのスレッドで8つのプロセスを実行すると、GCスレッドごとに2つのスレッドしか生成されず、GCはこれらのプロセス間で並列に動作できます。

ごみが少なく、より多くのCPUを直接使用するテストを作成すると、結果が異なる可能性があります。

(これが唯一の憶測であることに注意してください、私はあなたのテストを実行しなかった、と私はとにかくあなたの結果は異なるだろうデュアルコアCPUを持っているので)

+0

これは私がstringbuilderを次のようなものに置き換えた理由です: 'int x = i + j;int z = x ++; '、私は1つのプロセスと16スレッドで100%の負荷を得ました。 – Manuel

1

使用生成するThread.SpinWait(int.MaxValue)のようなものあなたのプログラムは主にメモリの負荷を発生させ、誤った共有などの影響をもたらす可能性があるため、CPU負荷が発生します。 CodeInChaosが既に述べたように、GCの活動はパフォーマンスに影響を与える可能性が非常に高いでしょう。

1

他のものと同様に、私はこれがGCと関係があると考えています。負荷の例では、StringBuilderのオブジェクトがで自分のデータを保存するには1GBサイズの配列を求めることになるループのための2つの終了によって、メモリの巨大な量を使用しています。

の理由のカップルは、GCスレッドができるとあります。処理を遅らせる。

VMがメモリ不足になるとすぐに、ほとんどのスレッドが中断され、GCがメモリを解放して続行するのを待つ必要があります。これは、すべてのスレッドがより多くのメモリを要求しているためです実行時にほぼ同時に)。

スレッドのコンテキスト切り替えには2番目の方法があります(これはおそらく最も大きな理由です)。コアXでスレッドAが実行されている場合、GCはコアXにロードするか、スレッドAのすべてのメモリをコアXのキャッシュから実行中のコアのキャッシュにロードする必要があります。どちらの方法でも、CPUはRAMからのメモリをキャッシュにロードするまで待つ必要があります。 RAMはハードドライブに比べて高速ですが、CPUと比較すると辛抱強く遅いです。また、CPUがRAMを応答するのを待っている間は、処理を実行できず、負荷が軽減されます。

複数のVMを使用している場合、各VMは独自のコアで動作し、他のVMの内容には気を付けません。そして、GCが呼び出されると、GCは他の2つのスレッドと同じコア上で実行できるので、コンテキストスイッチは必要ありません。

4

考慮すべき何か他のものは、異なるモードは、ガベージコレクタにあるということです。

  • サーバーGC
  • ワークステーションGC - 同時(asp.netのデフォルトexecept)
  • ワークステーションGC - 非同時

それぞれhereのグラフィックの詳細を見つけることができます。

プロセスはスレッドをたくさん使用しており、大量のメモリを割り当てているので、サーバーGCを試す必要があります。

は、サーバーGCは、一貫した負荷と要求 を割り当て、高速でメモリの割り当てを解除されているがあり サーバアプリケーションで高いスループットと高いスケーラビリティのために最適化されています。サーバーGCは、プロセッサごとに1つのヒープと1つのGCスレッドを で使用し、可能な限り ヒープの均衡をとろうとします。ガベージコレクションの時点で、GC スレッドはそれぞれのスレッドで動作し、特定の ポイントでランデブーします。彼らはすべて自分のヒープで動作するので最小限のロックなど が必要です。このタイプの状況では非常に効率的です。

あなたのApp.configファイルでサーバーCG有効にします。これが唯一のマルチプロセッサ(またはコア)システム上で動作することを

<configuration> 
<runtime> 
    <gcServer enabled="true" /> 
</runtime> 
</configuration> 

注意を。ウィンドウに1つのプロセッサしか報告されない場合、代わりにWorkstation GC - Non Concurrentが表示されます。

関連する問題