11

Linuxボックス(AMD 6コア、16 GB RAM)でJVM(Oracle 1.7 64ビット)をいじって、アプリケーションのスレッド数がパフォーマンスにどのように影響するかを確認しています。どの時点でコンテキストの切り替えがパフォーマンスを低下させるかを測定したいと考えています。

スレッド実行プールを作成する小さなアプリケーションを作成しました。

Executors.newFixedThreadPool(numThreads)

numThreadsプログラムを実行するたびに調整して、その効果を確認します。

numThread次に、ジョブ ( のインスタンスjava.util.concurrent.Callable) をプールに送信します。それぞれが をインクリメントしAtomicInteger、いくつかの作業を行い (ランダムな整数の配列を作成してシャッフルします)、しばらくスリープします。アイデアは、Web サービス呼び出しをシミュレートすることです。最後に、ジョブがプールに再送信されるので、常にnumThreadsジョブが機能します。

1 分あたりに処理されるジョブの数として、スループットを測定しています。

数千のスレッドで、1 分間に最大 400,000 のジョブを処理できます。8000 スレッドを超えると、結果が大きく変化し始め、コンテキストの切り替えが問題になっていることが示唆されます。しかし、スレッド数を 30,000 まで増やし続けても、スループットは高くなります (1 分あたり 420,000 ~ 570,000 ジョブ)。

ここで質問がありますjava.lang.OutOfMemoryError: Unable to create new native thread。約 31,000 件以上の求人があります。-Xmx6000M役に立たない設定を試しました。で遊んでみました-Xssが、それも役に立ちません。

私はそれulimitが役に立つかもしれないと読みましたが、増加してulimit -u 64000も何も変わりませんでした。

情報:

[root@apollo ant]# ulimit -a
core file size          (blocks, -c) 0
data seg size           (kbytes, -d) unlimited
scheduling priority             (-e) 0
file size               (blocks, -f) unlimited
pending signals                 (-i) 127557
max locked memory       (kbytes, -l) 64
max memory size         (kbytes, -m) unlimited
open files                      (-n) 1024
pipe size            (512 bytes, -p) 8
POSIX message queues     (bytes, -q) 819200
real-time priority              (-r) 0
stack size              (kbytes, -s) 8192
cpu time               (seconds, -t) unlimited
max user processes              (-u) 1024
virtual memory          (kbytes, -v) unlimited
file locks                      (-x) unlimited

質問 #1: より大きなスレッド プールを作成するには、どうすればよいでしょうか?

質問 2: どの段階で、コンテキストの切り替えによって実際にスループットが低下し、プロセスが停止することが予想されますか?


もう少し処理を行うように変更し (提案されたように)、平均応答時間の記録を開始した (提案されたように) 後のいくつかの結果を次に示します。

// ( (n_cores x t_request) / (t_request - t_wait) ) + 1
// 300 ms wait, 10ms work, roughly 310ms per job => ideal response time, 310ms
// ideal num threads = 1860 / 10 + 1 = 187 threads
//
// results:
//
//   100 =>  19,000 thruput,  312ms response, cpu < 50%
//   150 =>  28,500 thruput,  314ms response, cpu 50%
//   180 =>  34,000 thruput,  318ms response, cpu 60%
//   190 =>  35,800 thruput,  317ms response, cpu 65%
//   200 =>  37,800 thruput,  319ms response, cpu 70%
//   230 =>  42,900 thruput,  321ms response, cpu 80%
//   270 =>  50,000 thruput,  324ms response, cpu 80%
//   350 =>  64,000 thruput,  329ms response, cpu 90%
//   400 =>  72,000 thruput,  335ms response, cpu >90%
//   500 =>  87,500 thruput,  343ms response, cpu >95%
//   700 => 100,000 thruput,  430ms response, cpu >99%
//  1000 => 100,000 thruput,  600ms response, cpu >99%
//  2000 => 105,000 thruput, 1100ms response, cpu >99%
//  5000 => 131,000 thruput, 1600ms response, cpu >99%
// 10000 => 131,000 thruput, 2700ms response, cpu >99%,  16GB Virtual size
// 20000 => 140,000 thruput, 4000ms response, cpu >99%,  27GB Virtual size
// 30000 => 133,000 thruput, 2800ms response, cpu >99%,  37GB Virtual size
// 40000 =>       - thruput,    -ms response, cpu >99%, >39GB Virtual size => java.lang.OutOfMemoryError: unable to create new native thread

私はそれらを次のように解釈します。

1) アプリケーションが 96.7% の時間スリープしているにもかかわらず、まだ多くのスレッド スイッチングを実行する必要があります。 2) コンテキスト スイッチングは測定可能であり、応答時間に示されています。

ここで興味深いのは、アプリを調整するときに、許容できる応答時間 (たとえば 400 ミリ秒) を選択し、その応答時間が得られるまでスレッドの数を増やすことができるということです。この場合、アプリは約 95,000 の要求を処理できます。分。

理想的なスレッド数はコア数に近いとよく言われます。待機時間 (ブロックされたスレッド、データベースまたは Web サービスの応答を待機するなど) があるアプリでは、計算でそれを考慮する必要があります (上記の式を参照)。しかし、その理論上の理想でさえ、結果を見たり、特定の応答時間を調整したりすると、実際の理想ではありません。

4

2 に答える 2

7

java.lang.OutOfMemoryError: Unable to create new native thread with more than about 31,000 job が表示されます。-Xmx6000M を設定しようとしましたが、役に立ちません。-Xss で遊んでみましたが、それも役に立ちません。

スレッド スタックはヒープから割り当てられないため、-Xmx 設定は役に立ちません。

何が起こっているかというと、JVM が OS にスタックを保持するメモリ セグメント (ヒープ外!) を要求し、OS がその要求を拒否しているということです。これの最も可能性の高い理由は、ulimit または OS メモリ リソースの問題です。

  • 「データ セグメント サイズ」の ulimit は無制限であるため、問題になることはありません。

  • そのため、メモリ リソースが残ります。一度に 1Mb で 30,000 のスレッドは ~30Gb であり、これは実際の物理メモリよりもはるかに多くなります。私の推測では、30Gb の仮想メモリに十分なスワップ領域があると思いますが、境界を少し押し上げすぎました。

-Xss 設定が役立つはずですが、要求されたスタック サイズをデフォルト サイズの より小さくする必要があります1m。それに加えて、ハード最小サイズがあります。

質問 #1: より大きなスレッド プールを作成できるようにするには、どうすればよいですか?

デフォルトのスタック サイズを現在のサイズより小さくするか、使用可能な仮想メモリの量を増やします。(後者は、すでに深刻な過剰割り当てを行っているように見えるため、推奨されません。)

質問 2: どの段階で、コンテキストの切り替えによって実際にスループットが低下し、プロセスが停止することが予想されますか?

それを予測することは不可能です。スレッドが実際に何をしているかに大きく依存します。実際、ベンチマークを行っても、実際のマルチスレッド アプリケーションがどのように動作するかを示す答えが得られるとは思いません。


Oracle サイトでは、スレッド スタックスペースのトピックについて次のように述べています。

Java SE 6 では、Sparc のデフォルトは 32 ビット VM で 512k、64 ビット VM で 1024k です。x86 Solaris/Linux では、32 ビット VM で 320k、64 ビット VM で 1024k です。

Windows では、デフォルトのスレッド スタック サイズはバイナリ (java.exe) から読み取られます。Java SE 6 以降、この値は 32 ビット VM で 320k、64 ビット VM で 1024k です。

-Xss オプションを指定して実行すると、スタック サイズを減らすことができます。例えば:

  java -server -Xss64k

Windows の一部のバージョンでは、OS が非常に粗い粒度を使用してスレッド スタック サイズを切り上げる場合があることに注意してください。要求されたサイズがデフォルト サイズよりも 1K 以上小さい場合、スタック サイズはデフォルトに切り上げられます。それ以外の場合、スタック サイズは 1 MB の倍数に切り上げられます。

64k は、スレッドごとに許容されるスタック領域の最小量です。

于 2013-01-27T10:46:57.757 に答える
2

ここにいくつかのポイント/方法があります。

  1. コンテキスト スイッチで使用されるデータを見てみましょう。ブール値または文字列の代わりに、大きなリストまたはマップを使用してみてください。

  2. 開始時に固定プールを作成しようとする代わりに、キャッシュされたプールを試してください。

  3. 小さな作業を行った後にスレッドを消滅させるのではなく、スレッドを生きたままにして、小さな作業のチャンクを何度も実行できるようにします。

  4. スレッドの処理時間を長く保つようにしてください。

于 2013-01-27T09:26:08.627 に答える