私は、数値最適化問題のクラスを解決するためのJavaアプリケーションに取り組んでいます-より正確に言うと、大規模線形計画問題です。単一の問題は、並列に解決できる小さなサブ問題に分割できます。CPUコアよりも多くのサブ問題があるため、ExecutorServiceを使用して、各サブ問題を、ExecutorServiceに送信されるCallableとして定義します。サブ問題を解決するには、ネイティブライブラリ(この場合は線形計画法ソルバー)を呼び出す必要があります。
問題
最大44の物理コアと最大256gのメモリを備えたUnixおよびWindowsシステムでアプリケーションを実行できますが、大きな問題の場合、Windowsでの計算時間はLinuxでの計算時間よりも桁違いに長くなります。Windowsはかなり多くのメモリを必要とするだけでなく、時間の経過に伴うCPU使用率は最初の25%から数時間後には5%に低下します。Windowsのタスクマネージャのスクリーンショットは次のとおりです。
観察
- 問題全体の大規模なインスタンスの解決時間は数時間から数日の範囲で、最大32gのメモリを消費します(UNIXの場合)。副問題の解決時間はmsの範囲です。
- この問題は、数分で解決できる小さな問題では発生しません。
- Linuxはそのままの状態で両方のソケットを使用しますが、Windowsは、アプリケーションが両方のコアを利用するように、BIOSでメモリインターリービングを明示的にアクティブにする必要があります。ただし、これを行うかどうかは、時間の経過に伴う全体的なCPU使用率の低下には影響しません。
- VisualVMのスレッドを見ると、すべてのプールスレッドが実行されていますが、待機中のものはありません。
- VisualVMによると、90%のCPU時間はネイティブ関数呼び出しに費やされています(小さな線形プログラムを解く)
- アプリケーションは多くのオブジェクトを作成および逆参照しないため、ガベージコレクションは問題になりません。また、ほとんどのメモリはオフヒープに割り当てられているようです。最大のインスタンスでは、Linuxでは4g、Windowsでは8gのヒープで十分です。
私が試したこと
- あらゆる種類のJVM引数、高XMS、高メタスペース、UseNUMAフラグ、その他のGC。
- 異なるJVM(ホットスポット8、9、10、11)。
- さまざまな線形計画ソルバーのさまざまなネイティブライブラリ(CLP、Xpress、Cplex、Gurobi)。
ご質問
- ネイティブコールを多用する大規模なマルチスレッドJavaアプリケーションのLinuxとWindowsのパフォーマンスの違いは何が原因ですか?
- たとえば、Windowsに役立つ実装で変更できるものはありますか。たとえば、何千ものCallableを受け取るExecutorServiceの使用を避け、代わりに何をすべきでしょうか?
ForkJoinPool
は、手動スケジューリングよりも効率的です。
ForkJoinPool
代わりに試しましたExecutorService
か?問題がCPUバウンドの場合、25%のCPU使用率は本当に低くなります。