23

実行できる最適なスレッド数を知りたいのですが。通常、これはに等しくなりRuntime.getRuntime().availableProcessors()ます。

ただし、ハイパースレッディングをサポートするCPUでは、返される数は2倍になります。さて、いくつかのタスクではハイパースレッディングは良いですが、他のタスクでは何もしません。私の場合、それは何もしないのではないかと思うので、返された数Runtime.getRuntime().availableProcessors()を2で割る必要があるかどうかを知りたいと思います。

そのためには、CPUがハイパースレッディングであるかどうかを推測する必要があります。したがって、私の質問-Javaでそれを行うにはどうすればよいですか?

ありがとう。

編集

OK、コードのベンチマークを行いました。これが私の環境です:

  • Lenovo ThinkPad W510(つまり、4コアとハイパースレッディングを備えたi7 CPU)、16GのRAM
  • Windows 7
  • 105Mから16Mの範囲のzipサイズの84のzipCSVファイル
  • すべてのファイルはメインスレッドで1つずつ読み取られます。HDへのマルチスレッドアクセスはありません。
  • 各CSVファイルの行にはいくつかのデータが含まれており、そのデータは解析され、高速の文脈自由テストによって行が関連しているかどうかが判断されます。
  • 関連する各行には、2つのdouble(好奇心旺盛な人のために経度と緯度を表す)が含まれています。これらは強制的に1つにされLong、共有ハッシュセットに格納されます。

したがって、ワーカースレッドはHDから何も読み取りませんが、コンテンツの解凍と解析(opencsvライブラリを使用)に専念します。

以下は、退屈な詳細なしのコードです。

public void work(File dir) throws IOException, InterruptedException {
  Set<Long> allCoordinates = Collections.newSetFromMap(new ConcurrentHashMap<Long, Boolean>());
  int n = 6;
  // NO WAITING QUEUE !
  ThreadPoolExecutor exec = new ThreadPoolExecutor(n, n, 0L, TimeUnit.MILLISECONDS, new SynchronousQueue<Runnable>());
  StopWatch sw1 = new StopWatch();
  StopWatch sw2 = new StopWatch();
  sw1.start();
  sw2.start();
  sw2.suspend();
  for (WorkItem wi : m_workItems) {
    for (File file : dir.listFiles(wi.fileNameFilter)) {
      MyTask task;
      try {
        sw2.resume();
        // The only reading from the HD occurs here:
        task = new MyTask(file, m_coordinateCollector, allCoordinates, wi.headerClass, wi.rowClass);
        sw2.suspend();
      } catch (IOException exc) {
        System.err.println(String.format("Failed to read %s - %s", file.getName(), exc.getMessage()));
        continue;
      }
      boolean retry = true;
      while (retry) {
        int count = exec.getActiveCount();
        try {
          // Fails if the maximum of the worker threads was created and all are busy.
          // This prevents us from loading all the files in memory and getting the OOM exception.
          exec.submit(task);
          retry = false;
        } catch (RejectedExecutionException exc) {
          // Wait for any worker thread to finish
          while (exec.getActiveCount() == count) {
            Thread.sleep(100);
          }
        }
      }
    }
  }
  exec.shutdown();
  exec.awaitTermination(1, TimeUnit.HOURS);
  sw1.stop();
  sw2.stop();
  System.out.println(String.format("Max concurrent threads = %d", n));
  System.out.println(String.format("Total file count = %d", m_stats.getFileCount()));
  System.out.println(String.format("Total lines = %d", m_stats.getTotalLineCount()));
  System.out.println(String.format("Total good lines = %d", m_stats.getGoodLineCount()));
  System.out.println(String.format("Total coordinates = %d", allCoordinates.size()));
  System.out.println(String.format("Overall elapsed time = %d sec, excluding I/O = %d sec", sw1.getTime() / 1000, (sw1.getTime() - sw2.getTime()) / 1000));
}

public class MyTask<H extends CsvFileHeader, R extends CsvFileRow<H>> implements Runnable {
  private final byte[] m_buffer;
  private final String m_name;
  private final CoordinateCollector m_coordinateCollector;
  private final Set<Long> m_allCoordinates;
  private final Class<H> m_headerClass;
  private final Class<R> m_rowClass;

  public MyTask(File file, CoordinateCollector coordinateCollector, Set<Long> allCoordinates,
                Class<H> headerClass, Class<R> rowClass) throws IOException {
    m_coordinateCollector = coordinateCollector;
    m_allCoordinates = allCoordinates;
    m_headerClass = headerClass;
    m_rowClass = rowClass;
    m_name = file.getName();
    m_buffer = Files.toByteArray(file);
  }

  @Override
  public void run() {
    try {
      m_coordinateCollector.collect(m_name, m_buffer, m_allCoordinates, m_headerClass, m_rowClass);
    } catch (IOException e) {
      e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
    }
  }
}

結果を以下で見つけてください(繰り返し部分を省略するために出力を少し変更しました):

Max concurrent threads = 4
Total file count = 84
Total lines = 56395333
Total good lines = 35119231
Total coordinates = 987045
Overall elapsed time = 274 sec, excluding I/O = 266 sec

Max concurrent threads = 6
Overall elapsed time = 218 sec, excluding I/O = 209 sec

Max concurrent threads = 7
Overall elapsed time = 209 sec, excluding I/O = 199 sec

Max concurrent threads = 8
Overall elapsed time = 201 sec, excluding I/O = 192 sec

Max concurrent threads = 9
Overall elapsed time = 198 sec, excluding I/O = 186 sec

独自の結論を自由に引き出すことができますが、私の具体的なケースでは、ハイパースレッディングによってパフォーマンスが向上するということです。また、6つのワーカースレッドを持つことは、このタスクと私のマシンにとって正しい選択のようです。

4

7 に答える 7

5

残念ながら、これはJavaからは不可能です。アプリが最新のLinuxバリアントで実行されることがわかっている場合は、ファイル/ proc / cpuinfoを読み取って、HTが有効になっているかどうかを推測できます。

このコマンドの出力を読み取ると、次のようなトリックが実行されます。

grep -i "physical id" /proc/cpuinfo | sort -u | wc -l
于 2012-07-31T10:38:37.583 に答える
4

ハイパースレッディングがオンになっているか、ハイパースレッディングがオフになっているか、ハイパースレッディングがないかを判断する信頼できる方法はありません。

代わりに、より良いアプローチは、最初に実行するとき(または毎回)に最初のキャリブレーションを実行することです。これにより、使用するアプローチを決定する最初のテストが実行されます。

別のアプローチは、ハイパースレッディングが役に立たない場合でもすべてのプロセッサを使用することです(コードが劇的に遅くならない場合)

于 2012-07-31T11:15:33.130 に答える
3

もう少し考えてみてください。

  • ハイパースレッディングには、コードごとに2つ以上のスレッドが含まれる場合があります(Sparcには8つある場合があります)
  • ガベージコレクターも機能するためにCPU時間を必要とします。
  • ハイパースレッディングは並行GCに役立つ場合があります-またはそうでない場合があります。または、JVMがコアの排他的(ハイパースレッディングではない)所有者を要求する場合があります。したがって、テスト中により良い結果を得るためにGCを妨げることは、長期的には害を及ぼす可能性があります。
  • ハイパースレッディングは通常、キャッシュミスがある場合に役立ちます。そのため、CPUは停止せず、別のタスクに切り替えられます。したがって、「ハイパースレッディングを行うかどうか」は、ワークロードとCPU L1/L2キャッシュサイズ/メモリ速度などの両方に依存します。
  • OSは一部のスレッドに対してバイアスをかけている可能性があり、Thread.setPriorityは尊重されない可能性があります(Linuxでは通常は尊重されません)。
  • プロセスのアフィニティを設定して、一部のコアを許可しないようにすることができます。したがって、ハイパースレッディングがあることを知っていても、そのような場合には大きなメリットはありません。

そうは言っても、ワーカースレッドのサイズを設定し、アーキテクチャの詳細を考慮してセットアップ方法を推奨する必要があります。

于 2012-08-01T10:05:24.270 に答える
1

純粋なJavaからそれを判断する方法はありません(HTを使用して実装されているかどうかにかかわらず、結局のところ論理コアはコアです)。これまでに提案されたソリューションは(あなたが尋ねたように)要件を解決できることに注意してください。ただし、Intel CPUだけがハイパースレッディングの形式を提供するわけではありません(Sparcが思い浮かび、他にもあると確信しています)。

また、システムがHTを使用していると判断した場合でも、Javaのコアとのスレッドアフィニティを制御できないことも考慮していません。したがって、OSのスレッドスケジューラに翻弄されます。(キャッシュのゴミ箱を減らすために)スレッドのパフォーマンスが向上する可能性のあるシナリオはありますが、使用するスレッドの数を静的に決定する方法はありません(すべてのCPUのキャッシュサイズが大きく異なるため(ローエンドで256KBの範囲)最近では、サーバーで16MBを超えることが合理的に予想されます。これは、世代ごとに変わるはずです)。

設定可能な設定にするだけで、ターゲットシステムを正確に知らずにこれを決定しようとしても無駄です。

于 2012-07-31T11:48:41.917 に答える
1

の場合Windows、論理コアの数がコアの数よりも多い場合は、hyper-threading有効になっています。詳しくはこちらをご覧ください。

wmicこの情報を見つけるために使用できます。

C:\WINDOWS\system32>wmic CPU Get NumberOfCores,NumberOfLogicalProcessors /Format:List


NumberOfCores=4
NumberOfLogicalProcessors=8

したがって、私のシステムにはhyper-threading。論理プロセッサの数はコアの2倍です。

しかし、あなたは知る必要さえないかもしれません。Runtime.getRuntime().availableProcessors()すでに論理プロセッサの数を返します。

物理コア数を取得するための完全な例(Windowsのみ):

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class PhysicalCores
{
    public static void main(String[] arguments) throws IOException, InterruptedException
    {
        int physicalNumberOfCores = getPhysicalNumberOfCores();
        System.out.println(physicalNumberOfCores);
    }

    private static int getPhysicalNumberOfCores() throws IOException, InterruptedException
    {
        ProcessBuilder processBuilder = new ProcessBuilder("wmic", "CPU", "Get", "NumberOfCores");
        processBuilder.redirectErrorStream(true);
        Process process = processBuilder.start();
        String processOutput = getProcessOutput(process);
        String[] lines = processOutput.split(System.lineSeparator());
        return Integer.parseInt(lines[2]);
    }

    private static String getProcessOutput(Process process) throws IOException, InterruptedException
    {
        StringBuilder processOutput = new StringBuilder();

        try (BufferedReader processOutputReader = new BufferedReader(
                new InputStreamReader(process.getInputStream())))
        {
            String readLine;

            while ((readLine = processOutputReader.readLine()) != null)
            {
                processOutput.append(readLine);
                processOutput.append(System.lineSeparator());
            }

            process.waitFor();
        }

        return processOutput.toString().trim();
    }
}
于 2018-07-24T13:52:59.013 に答える
0

それを行う方法はありません。実行できることの1つはRuntime.getRuntime().availableProcessors()、アプリケーションにスレッドのスレッドプールを作成し、リクエストが届いたときと同じように使用することです。

このようにして、0-Runtime.getRuntime().availableProcessors()スレッド数を設定できます。

于 2012-07-31T11:03:21.947 に答える
0

OSまたはランタイムを確実に照会できない場合がありますが、簡単なベンチマークを実行することはできます。

スピンロックスレッドを徐々に増やし、新しいスレッドが以前と同じように繰り返されるかどうかをテストします。スレッドの1つのパフォーマンスが以前の各テストの約半分未満になると(少なくともIntelの場合、SPARCについてはわかりません)、ハイパースレッドとコアを共有し始めたことがわかります。

于 2018-05-02T05:08:22.997 に答える