4

私は、キューで作業を受け入れ、その作業をスピンオフして独立したスレッドで完了するアプリケーションを持っています。スレッドの数は、最大 100 など、それほど多くはありませんが、これらは集中的なタスクであり、CPU をすぐに 100% まで上昇させる可能性があります。

最大限の作業を最速で行うには: より多くの作業を行う必要があるときに、より多くのスレッドを起動し、Java スレッド スケジューラに作業の分散を任せるのが最善でしょうか。 100% より速く取得できますか?

マシンは私のJavaアプリ専用です。

編集:

素晴らしいインプットをありがとう!

タスクはさまざまな複雑さを持ち、I/O を伴うため、たとえば 4 という低いスレッド プールを使用すると、CPU が 20% しか実行されない可能性があります。実際に CPU を 100% にするタスクの数を知る方法はありません。

私の考えでは、RMI を介して CPU を監視し、作業を動的に上下にダイヤルするか、それとも気にせず OS に処理させるべきかということでした。

4

5 に答える 5

15

並列スレッドで計算負荷の高い同時タスクが多すぎると、すぐに収益が減少するポイントに到達します。実際、N 個のプロセッサ (コア) がある場合、そのようなスレッドは N 個以上必要ではありません。ここで、I/O やユーザーの操作のためにタスクがときどき一時停止する場合は、適切な数がいくらか大きくなる可能性があります。しかし、一般に、計算を実行したいスレッドの数が使用可能なコア数よりも多い場合、プログラムはコンテキスト スイッチで時間を浪費しています。つまり、スケジューリングにコストがかかります。

于 2012-04-12T02:11:10.663 に答える
7

CPU が 100% 稼働しているという事実は、CPU が有用な作業をどれだけビジー状態で行っているかを示すものではありません。あなたの場合、コアよりも多くのスレッドを使用しているため、100% にはコンテキストの切り替えが含まれており、不必要にメモリを使用しています (100 スレッドの場合はわずかな影響)。これは最適ではありません。

CPU を集中的に使用するタスクでは、通常、次のイディオムを使用します。

private final int NUM_THREADS = Runtime.getRuntime().availableProcessors() + 1;
private final ExecutorService executor = Executors.newFixedThreadPool(NUM_THREADS);

他の人が示したように、より多くのスレッドを使用すると、不要なコンテキスト切り替えが導入されるだけです。

明らかに、タスクが何らかの I/O やその他のブロック操作を行う場合、これは当てはまらず、より大きなプールが理にかなっています。

編集

@MartinJames のコメントに返信するために、(単純な) ベンチマークを実行しました。結果は、プール サイズ = プロセッサ数 + 1 から 100 にすると、パフォーマンスがわずかに低下する (5% としましょう) ことを示しています。 1000 および 10000) は、パフォーマンスに大きな影響を与えます。

結果は 10 回の実行の平均です。
プール サイズ: 9: 238 ミリ秒。//(NUM_CORES+1)
プール サイズ: 100: 245 ミリ秒。
プール サイズ: 1000: 319 ミリ秒。
プール サイズ: 10000: 2482 ミリ秒。

コード:

public class Test {

    private final static int NUM_CORES = Runtime.getRuntime().availableProcessors();
    private static long count;
    private static Runnable r = new Runnable() {

        @Override
        public void run() {
            int count = 0;
            for (int i = 0; i < 100_000; i++) {
                count += i;
            }
            Test.count += count;
        }
    };

    public static void main(String[] args) throws Exception {
        //warmup
        runWith(10);

        //test
        runWith(NUM_CORES + 1);
        runWith(100);
        runWith(1000);
        runWith(10000);
    }

    private static void runWith(int poolSize) throws InterruptedException {
        long average = 0;
        for (int run = 0; run < 10; run++) { //run 10 times and take the average
            Test.count = 0;
            ExecutorService executor = Executors.newFixedThreadPool(poolSize);
            long start = System.nanoTime();
            for (int i = 0; i < 50000; i++) {
                executor.submit(r);
            }
            executor.shutdown();
            executor.awaitTermination(10, TimeUnit.SECONDS);
            long end = System.nanoTime();
            average += ((end - start) / 1000000);
            System.gc();
        }
        System.out.println("Pool size: " + poolSize + ": " + average / 10 + " ms.  ");
    }
}
于 2012-04-12T08:39:10.437 に答える
7

最大限の作業を最速で行うには: より多くの作業を行う必要があるときに、より多くのスレッドを起動し、Java スレッド スケジューラに作業の分散を任せるのが最善でしょうか。 100% より速く取得できますか?

スレッドを追加すると、コンテキスト切り替え、メモリ キャッシュのフラッシュ、メモリ キャッシュのオーバーフロー、およびカーネルと JVM スレッドの管理で発生するオーバーヘッドが増加します。スレッドが CPU を占有すると、カーネルの優先度がある程度低下し、タイム スライスの最小値に達します。ますます多くのスレッドがメモリを混雑させると、さまざまな内部 CPU メモリ キャッシュがオーバーフローします。CPU がより遅いメモリからジョブをスワップする必要がある可能性が高くなります。JVM の内部では、より多くのミューテックス ローカル競合が発生し、スレッドごとの (場合によっては小さい) 増分 GC オーバーヘッドとオブジェクト帯域幅 GC オーバーヘッドが発生する可能性があります。ユーザー タスクの同期の程度によっては、スレッドが増えると、メモリのフラッシュとロックの競合が増加します。

どのようなプログラムやアーキテクチャでも、カーネルと JVM のオーバーヘッドを制限しながら、スレッドが使用可能なプロセッサと IO リソースを最適に利用できるスイート スポットがあります。そのスイート スポットを繰り返し見つけるには、何度も反復し、当て推量を行う必要があります。

Executors.newFixedThreadPool(SOME_NUMBER);を使用してジョブを送信することをお勧めします。次に、ボックスの作業とアーキテクチャに応じて、同時に実行される最適な数のプールが見つかるまで、スレッドの数を上下に変更して複数の実行を行うことができます。

ただし、最適なスレッド数は、プロセッサーの数やその他の重要な要因によって異なることを理解しておいてください。ディスクまたはネットワーク IO リソースでブロックしている場合は、より多くのスレッドが必要になることがあります。実行している作業がほとんど CPU ベースの場合、スレッドは少なくなります。

于 2012-04-12T02:11:08.380 に答える
2

「よりスマートになり、作業負荷を管理して CPU を 100% 未満に保つと、さらに高速になりますか?」

おそらくそうではありません。

他の人が投稿したように、ほとんどのタスクが CPU を集中的に使用する場合、100 スレッドはスレッドプールには多すぎます。典型的なシステムでは、パフォーマンスに大きな違いはありません。過負荷が大きいと、4 スレッドで悪くなり、400 で悪くなります。

どのようにして 100 スレッドを決定したのですか? なぜ16ではないのですか?

「スレッドの数はそれほど大きくありません。たとえば、最大 100 です」 - 変動しますか? 起動時に 16 を作成し、それらの管理を停止するだけです。それらにキューを渡して、それらのことは忘れてください。

恐ろしい考えです - あなたはタスクごとに新しいスレッドを作成していませんよね?

于 2012-04-12T09:25:55.243 に答える
0

100% の使用率を維持する必要がありますが、スレッドの数は最小限に抑えてください。100 スレッドは多すぎます。

于 2012-04-12T05:01:16.513 に答える