Подтвердить что ты не робот

Можно ли проверить Java, если процессор является гиперпотоком?

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

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

Для этого я должен определить, является ли процессор гиперпотоком. Отсюда мой вопрос - как я могу сделать это на Java?

Спасибо.

ИЗМЕНИТЬ

Хорошо, я проверил свой код. Вот моя среда:

  • Lenovo ThinkPad W510 (т.е. процессор i7 с 4 ядрами и гиперпотоком), 16 ГБ оперативной памяти
  • Windows 7
  • 84 ZIP файла CSV с размерами от 10М до 16М
  • Все файлы читаются один за другим в основном потоке - нет многопоточного доступа к HD.
  • Каждая строка файла 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 рабочих потоков, по-видимому, является правильным выбором для этой задачи и моей машины.

4b9b3361

Ответ 1

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

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

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

Ответ 2

Не существует надежного способа определить, включена ли гиперпоточность, гиперпоточность, которая отключена или нет гиперпотоков.

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

Другим подходом является использование всех процессоров, даже если гиперпоточность не помогает (при условии, что код не делает код значительно медленнее)

Ответ 3

Немногие размышления:

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

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

Ответ 4

Невозможно определить, что из чистой Java (ведь логическое ядро ​​- это ядро, если оно реализовано с использованием HT или нет). Помните, что предлагаемые решения могут решить ваше требование (как вы просили), но не только процессор Intel предлагает форму гиперпотока (Sparc приходит на ум, и я уверен, что есть и другие).

Вы также не учли, что даже если вы определяете, что система использует HT, вы не сможете контролировать сходство потоков с ядрами Java. Таким образом, вы по-прежнему находитесь во власти планировщика потоков ОС. Несмотря на то, что существуют вероятные сценарии, в которых меньшее количество потоков может работать лучше (из-за сокращения кэширования), нет способа определить статически, сколько потоков должно использоваться (ведь у ЦП есть очень разные размеры кеша (диапазон от 256 КБ на нижнем конце до > 16 МБ на серверах можно разумно ожидать в настоящее время, и это обязательно изменится с каждым новым поколением).

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

Ответ 5

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

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