CPUコアに従ってスレッドをスケーリングする方法は?


107

Javaの複数のスレッドで数学的な問題を解決したいと思います。私の数学の問題は、複数のスレッドで解決したい作業単位に分割できます。

一定量のスレッドを処理するのではなく、CPUコアの量と一致する量のスレッドを使用する必要があります。私の問題は、インターネットで簡単なチュートリアルを見つけることができなかったことです。私が見つけたすべては、固定スレッドの例です。

これはどのように行うことができますか?例を提供できますか?

回答:


119

静的なランタイムメソッドavailableProcessorsを使用して、Java仮想マシンで使用可能なプロセスの数を確認できます。利用可能なプロセッサの数を決定したら、その数のスレッドを作成し、それに応じて作業を分割します。

更新:さらに明確にするために、スレッドはJavaの単なるオブジェクトなので、他のオブジェクトを作成するのと同じように作成できます。したがって、上記のメソッドを呼び出して、2つのプロセッサを返すことがわかったとします。驚くばかり。これで、新しいスレッドを生成し、そのスレッドの作業を分割して、スレッドを起動するループを作成できます。これが私が何を意味するのかを示すためのいくつかの擬似コードです:

int processors = Runtime.getRuntime().availableProcessors();
for(int i=0; i < processors; i++) {
  Thread yourThread = new AThreadYouCreated();
  // You may need to pass in parameters depending on what work you are doing and how you setup your thread.
  yourThread.start();
}

独自のスレッドの作成の詳細については、このチュートリアルをご覧ください。また、スレッドの作成については、スレッドプールを確認することもできます。


17
これは基本的には正しいですが、Intelの「ハイパースレッディング」で販売されているプロセッサのパフォーマンスに注意してください。クアッドコアでは、これは4ではなく8を返しますが、実際には4スレッド後にパフォーマンスが低下し始める可能性があるため、私の独自のベンチマークで教えてください:)
xcut

こんにちは、わかりました、これが可能であることを知りませんでした。しかし、1つのタスクを複数のワークユニットに分割し、最終的なワークステップのすべての部分ソリューションが必要な場合、これはどのように行われますか?いくつかの「yourThreads」がある場合、これにjoin()を使用する方法がわかりません。これらのいくつかのスレッドはどのように区別されますか?:) BTW:スレッドプーリングへのリンクにより、ibm.com /
developerworks / library / j

5
ここの例を見てください:java.sun.com/j2se/1.5.0/docs/api/java/util/concurrent/… スレッドプールを作成および管理するより効率的な方法を教えてくれます...最初はより複雑ですが、ほとんどの場合と同様に、より単純な場合はすぐに制限に達するため、より複雑になります。
Bill K

62

おそらく、これについてもjava.util.concurrentフレームワークを見たいと思うでしょう。何かのようなもの:

ExecutorService e = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
// Do work using something like either
e.execute(new Runnable() {
        public void run() {
            // do one task
        }
    });

または

    Future<String> future = pool.submit(new Callable<String>() {
        public String call() throws Exception {
            return null;
        }
    });
    future.get();  // Will block till result available

これは、独自のスレッドプールなどに対処するよりもはるかに優れています。


こんにちはDaveC、うーん、それは以前に知らなかったので、これを見てみましょう。また、利用可能なCPUコアに応じてスケーリングできますか?あなたの短い例ではそれを見ることができないからです。よろしく、アンドレアス
アンドレアスホルニッヒ

3
java.util.concurrentは非常にスケーラブルです
Kristopher Ivesが

4
多くの場合、利用可能なプロセッサの数を備えた固定サイズのプールは、CPUバウンドプロセスに最適です。ここの最初の例はあなたがする必要があるすべてです。
Peter Lawrey、

1
受け入れられた回答の最初のコメントで述べたように、2つの理由により、報告された「プロセッサー」の半分の数を使用する方が良いでしょう。 、および2.それは、システムの残りの部分(OSおよび他のプログラム)が機能するためのある程度の処理能力を可能にします。
Matthieu 2013

10

オプション1:

newWorkStealingPoolからExecutors

public static ExecutorService newWorkStealingPool()

使用可能なすべてのプロセッサをターゲットの並列処理レベルとして使用して、ワークスチールスレッドプールを作成します。

このAPIでは、コアの数をに渡す必要はありませんExecutorService

grepcodeからのこのAPIの実装

/**
     * Creates a work-stealing thread pool using all
     * {@link Runtime#availableProcessors available processors}
     * as its target parallelism level.
     * @return the newly created thread pool
     * @see #newWorkStealingPool(int)
     * @since 1.8
     */
    public static ExecutorService newWorkStealingPool() {
        return new ForkJoinPool
            (Runtime.getRuntime().availableProcessors(),
             ForkJoinPool.defaultForkJoinWorkerThreadFactory,
             null, true);
    }

オプション2:

newFixedThreadPoolのからのAPI Executorsother newXXX constructors、返品ExecutorService

public static ExecutorService newFixedThreadPool(int nThreads)

nThreadsを Runtime.getRuntime().availableProcessors()

オプション3:

ThreadPoolExecutor

public ThreadPoolExecutor(int corePoolSize,
                      int maximumPoolSize,
                      long keepAliveTime,
                      TimeUnit unit,
                      BlockingQueue<Runnable> workQueue)

Runtime.getRuntime().availableProcessors()パラメータとしてに渡しますmaximumPoolSize



4

標準的な方法は、Runtime.getRuntime()。availableProcessors()メソッドです。ほとんどの標準CPUでは、最適なスレッド数(実際のCPUコア数ではない)がここに返されます。したがって、これはあなたが探しているものです。

例:

ExecutorService service = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());

次のようにexecutorサービスをシャットダウンすることを忘れないでください(そうしないと、プログラムは終了しません)。

service.shutdown();

ここでは、将来ベースのMTコードを設定する方法の概要を簡単に説明します(例として、トピック外)。

CompletionService<YourCallableImplementor> completionService = 
    new ExecutorCompletionService<YourCallableImplementor>(service);
    ArrayList<Future<YourCallableImplementor>> futures = new ArrayList<Future<YourCallableImplementor>>();
    for (String computeMe : elementsToCompute) {
        futures.add(completionService.submit(new YourCallableImplementor(computeMe)));
    }

次に、予想される結果の数を追跡し、次のようにそれらを取得する必要があります。

try {
  int received = 0;
  while (received < elementsToCompute.size()) {
     Future<YourCallableImplementor> resultFuture = completionService.take(); 
     YourCallableImplementor result = resultFuture.get();
     received++; 
  }
} finally {
  service.shutdown();
}

2
最終的にシャットダウンの呼び出しを試す必要があります
Christophe Roussy 2017

1
@ChristopheRoussy正解です。スニペットを適宜変更しました。ありがとうございます。
fl0w 2017

3

Runtimeクラスには、availableProcessors()というメソッドがあります。これを使用して、CPUの数を把握できます。プログラムはCPUバウンドであるため、使用可能なCPUごとに(最大で)1つのスレッドが必要になるでしょう。


こんにちはJasonとEric(基本的に同じなので、両方の回答に1つのコメントを使用します)。さて、それはチェックするのがいいですが、これは最初の部分でしょう。コア数がわかったら、このコア数と同じくらいスレッドを可変にする必要があります。openbook.galileodesign.de/javainsel5/…(ドイツ語!)の前にこの例を試しましたが、固定スレッドを使用しています。しかし、デュアルコア環境では2コア、クアッドコア環境では4コアを使用して同じプログラミングをしたいと考えています。手動で変更したくありません。これは可能ですか?どうも!:)
Andreas Hornig

@Andreas-私の投稿に加えた更新を確認します。問題を明確にするのに役立つと思います。
JasCav、2009
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.