Можно ли проверить Java, если CPU является hyper threading?

Я хотел бы знать оптимальное количество потоков, которые я могу запустить. Обычно это равно Runtime.getRuntime().availableProcessors().

однако возвращаемое число в два раза выше на CPU, поддерживающем hyper threading. Теперь для некоторых задач hyper threading хорош, но для других он ничего не делает. В моем случае, я подозреваю, он ничего не делает, и поэтому я хочу знать, должен ли я разделить число, возвращаемое Runtime.getRuntime().availableProcessors() в два.

для этого я должен вывести, является ли CPU hyper threading. Следовательно мой вопрос - как я могу это сделать на Java?

спасибо.

редактировать

хорошо,я проверил свой код. Вот мое окружение:

  • Lenovo ThinkPad W510 (т. е. процессор i7 с 4 ядрами и гиперпоточностью), 16G ОЗУ
  • Windows 7
  • 84 сжатых CSV-файлов с размерами от 105M до 16M
  • все файлы считываются один за другим в главном потоке - нет многопоточного доступа к ЖЕСТКИЙ ДИСК.
  • каждая строка CSV-файла содержит некоторые данные, которые анализируются, и быстрый контекстный тест определяет, является ли строка актуальной.
  • каждая соответствующая строка содержит два двойника (представляющие долготу и широту, для любопытных), которые принуждаются к одному 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

вы вольны делать свои собственные выводы, но мое заключается в том, что технология HyperThreading повышает производительность в моем конкретном случае. Кроме того, наличие 6 рабочих потоков кажется правильным выбором для этой задачи и моей машины.

7 ответов


на Windows, если количество логических ядер больше, чем количество ядер, у вас есть hyper-threading включено. Подробнее об этом здесь.

можно использовать wmic найти эту информацию:

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


NumberOfCores=4
NumberOfLogicalProcessors=8

таким образом, моя система hyper-threading. Количество логических процессоров вдвое превышает количество ядер.

но вам, возможно, даже не нужно знать. 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();
    }
}

к сожалению, это невозможно из java. Если вы знаете, что приложение будет работать в современном варианте linux, вы можете прочитать файл /proc/cpuinfo и сделать вывод, включен ли HT.

чтение вывода этой команды делает трюк:

grep -i "physical id" /proc/cpuinfo | sort -u | wc -l

нет надежного способа определить, есть ли у вас hyper threading, который включен, hyper threading, который выключен или нет hyper threading.

вместо этого лучше сделать первой калибровки при первом запуске (или каждый раз), который запускает первый тест, который определяет, какой подход использовать.

другой подход-использовать все процессоры, даже если hyper threading не помогает (при условии, что он не делает код значительно медленнее)


несколько размышлений:

  • Hyperthreading может иметь более 2 потоков на код (Sparc может иметь 8)
  • сборщик мусора требует времени процессора для работы.
  • Hyperthreading может помочь параллельному GC - или не может; или JVM может запросить быть исключительным (не hyperthreading) владельцем ядра. Таким образом, препятствие GC для получения лучших результатов во время теста может повредить в долгосрочной перспективе.
  • Hyperthreading обычно полезно, если есть кэш-пропуски, поэтому процессор не заглох, а переключился на другую задачу. Следовательно, "к гиперпоточности или нет" будет зависеть как от рабочей нагрузки, так и от размера кэша/скорости памяти CPU L1/L2 и т. д.
  • ОС могут иметь смещение к / Против некоторых потоков и потоков.setPriority может не соблюдаться (в Linux это обычно не соблюдается).
  • можно установить сродство процесса, запрещая некоторые ядра. Таким образом, знание того, что есть гиперпространство, не будет иметь какой-либо существенной добродетели в таком случаи.

при этом: у вас должен быть параметр для размера рабочих потоков и рекомендации по настройке с учетом специфики архитектуры.


нет способа определить, что из чистой Java (ведь логическое ядро является ядром, если оно реализовано с использованием HT или нет). Остерегайтесь, что предлагаемые до сих пор решения могут решить ваши требования (как вы просили), но не только Intel CPU предлагает форму гиперпотока (Sparc приходит на ум, и я уверен, что есть и другие).

вы также не учли, что даже Если вы определяете, что система использует HT, вы не сможете управлять потоками сродство с ядрами из Java. Таким образом, вы все еще находитесь во власти планировщика потоков ОС. Хотя есть правдоподобные сценарии, в которых меньше потоков может работать лучше (из-за уменьшения Кеша), нет способа определить статически, сколько потоков должно использоваться (после того, как все CPU имеют очень различные размеры кэша (диапазон от 256KB на нижнем конце до >16MB на серверах можно разумно ожидать в настоящее время. И это обязательно изменится с каждым новым поколение.)

просто настраиваемые параметры, любая попытка определить это, не зная цели безрезультатн.


нет способа сделать это, одна вещь, которую вы можете сделать, это создать пул потоков Runtime.getRuntime().availableProcessors() потоки в вашем приложении и использовать, как при поступлении запроса.

таким образом, вы можете иметь 0 - Runtime.getRuntime().availableProcessors() количество потоков.


возможно, вы не сможете надежно запросить ОС или среду выполнения, но вы можете запустить быстрый тест.

постепенно увеличивайте потоки спин-блокировки, проверяя, повторяется ли каждый новый поток, а также предыдущий. Как только производительность одного из потоков меньше половины каждого из предыдущих тестов (по крайней мере, для intel, я не знаю о SPARC), вы знаете, что начали делиться ядром с hyperthread.