web-dev-qa-db-ja.com

マルチスレッドのCPU使用率が低すぎるJava Windows上のアプリケーション

私はJava数値最適化問題のクラスを解決するためのアプリケーション-より正確に言うと大規模な線形計画問題に取り組んでいます。単一の問題は、以下で解決できる小さなサブ問題に分割できます。並列。CPUコアよりも多くのサブ問題があるため、ExecutorServiceを使用して、ExecutorServiceに送信されるCallableとして各サブ問題を定義します。サブ問題を解決するには、ネイティブライブラリ(この場合は線形プログラミングソルバー)を呼び出す必要があります。

問題

最大44の物理コアと最大256gのメモリを備えたUnixおよびWindowsシステムでアプリケーションを実行できますが、大きな問題の場合、Windowsでの計算時間はLinuxでの計算時間よりも桁違いに長くなります。 Windowsはかなり多くのメモリを必要とするだけでなく、時間の経過に伴うCPU使用率は、最初の25%から数時間後には5%に低下します。 Windowsのタスクマネージャのスクリーンショットは次のとおりです。

Task Manager CPU utilization

観察

  • 問題全体の大規模なインスタンスの解決時間は数時間から数日の範囲で、最大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の使用を避け、代わりに何をすべきでしょうか?
18
Nils

Windowsの場合、プロセスあたりのスレッド数はプロセスのアドレススペースによって制限されます( Mark Russinovich-Windowsの限界に挑む:プロセスとスレッド も参照)。これが限界に近づくと、これが副作用を引き起こすと考えてください(コンテキストスイッチのスローダウン、断片化...)。 Windowsの場合、作業負荷を一連のプロセスに分割しようとします。私が数年前に抱えていた同様の問題について、Javaライブラリを実装してこれをより便利に(Java 8)しました)、次のように見てください: 外部プロセス

2
geri

ウィンドウのように聞こえるのは、しばらくの間メモリが変更されなかった後、ページファイルにメモリをキャッシュしているため、ディスク速度によってCPUがボトルネックになる理由です。

プロセスエクスプローラーでそれを確認し、キャッシュされているメモリの量を確認できます

0
Jew

このパフォーマンスの違いは、O.S。スレッドを管理します。 JVMはすべてのOSの違いを隠します。 this のように、それについて読むことができる多くのサイトがあります。しかし、違いがなくなるわけではありません。

Java 8+ JVMで実行していると思います。このため、ストリームおよび関数型プログラミング機能を使用することをお勧めします。関数型プログラミングは、小さな独立した問題が多数ある場合に非常に役立ちます順次実行から並列実行に簡単に切り替える必要があります。朗報は、ExecutorServiceのように、管理する必要のあるスレッドの数を決定するポリシーを定義する必要がないことです。たとえば、(- ここ ):

package com.mkyong.Java8;

import Java.util.ArrayList;
import Java.util.List;
import Java.util.stream.IntStream;
import Java.util.stream.Stream;

public class ParallelExample4 {

    public static void main(String[] args) {

        long count = Stream.iterate(0, n -> n + 1)
                .limit(1_000_000)
                //.parallel()   with this 23s, without this 1m 10s
                .filter(ParallelExample4::isPrime)
                .peek(x -> System.out.format("%s\t", x))
                .count();

        System.out.println("\nTotal: " + count);

    }

    public static boolean isPrime(int number) {
        if (number <= 1) return false;
        return !IntStream.rangeClosed(2, number / 2).anyMatch(i -> number % i == 0);
    }

}

結果:

通常のストリームの場合、1分10秒かかります。並列ストリームの場合、23秒かかります。 P7はi7-7700、16G RAM、Windows 10でテスト済み

したがって、Java)で関数プログラミング、ストリーム、ラムダ関数について読んで、コードで少数のテストを実装することをお勧めします(この新しいコンテキストで動作するように適応されています)。

0
xcesco

システム統計を投稿していただけませんか?タスクマネージャーは、それが利用可能な唯一のツールである場合、いくつかの手掛かりを提供するのに十分です。タスクがIO=を待っているかどうかを簡単に確認できます。これは、あなたが説明した内容に基づいて犯人のように聞こえます。特定のメモリ管理の問題が原因であるか、ライブラリが一時データを書き込む可能性がありますディスクなどへ.

CPU使用率の25%と言っているのは、同時に動作しているコアの数が少ないということですか? (すべてのコアが時々動作するが、同時に動作しない場合があります。)システムで実際に作成されているスレッド(またはプロセス)の数を確認しますか?数は常にコアの数よりも大きいですか?

十分なスレッドがある場合、それらの多くはアイドル状態で何かを待っていますか? trueの場合、割り込みを試みる(またはデバッガーをアタッチする)ことで、それらが待機しているものを確認できます。

0
Xiao-Feng Li