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 サービスの応答を待機するなど) があるアプリでは、計算でそれを考慮する必要があります (上記の式を参照)。しかし、その理論上の理想でさえ、結果を見たり、特定の応答時間を調整したりすると、実際の理想ではありません。