如果CPU是超线程,是否可以检查Java?

我想知道我可以运行的最佳线程数。 通常,这等于Runtime.getRuntime().availableProcessors()

但是,在支持超线程的CPU上,返回的数字是两倍。 现在,对于某些任务,超线程是好的,但对于其他任务,它什么都不做。 在我的情况下,我怀疑,它什么都不做,所以我想知道我是否必须将Runtime.getRuntime().availableProcessors()返回的数字除以二。

为此,我必须推断CPU是否是超线程。 因此我的问题 – 我怎么能用Java做到这一点?

谢谢。

编辑

好的,我已对我的代码进行了基准测试。 这是我的环境:

  • 联想ThinkPad W510(即带有4核和超线程的i7 CPU),16G内存
  • Windows 7的
  • 84个压缩CSV文件,压缩大小从105M到16M不等
  • 所有文件都在主线程中逐个读取 – 没有multithreading访问HD。
  • 每个CSV文件行包含一些数据,这些数据被解析,快速无上下文测试确定该行是否相关。
  • 每个相关的行包含两个双精度表示(好奇的经度和纬度),它们被强制转换为单个Long ,然后存储在共享散列集中。

因此,工作线程不会从HD中读取任何内容,但它们会通过解压缩和解析内容(使用opencsv库)来占用自己。

以下是代码,没有枯燥的细节:

 public void work(File dir) throws IOException, InterruptedException { Set allCoordinates = Collections.newSetFromMap(new ConcurrentHashMap()); int n = 6; // NO WAITING QUEUE ! ThreadPoolExecutor exec = new ThreadPoolExecutor(n, n, 0L, TimeUnit.MILLISECONDS, new SynchronousQueue()); 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> implements Runnable { private final byte[] m_buffer; private final String m_name; private final CoordinateCollector m_coordinateCollector; private final Set m_allCoordinates; private final Class m_headerClass; private final Class m_rowClass; public MyTask(File file, CoordinateCollector coordinateCollector, Set allCoordinates, Class headerClass, Class 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个工作线程似乎是此任务和我的机器的正确选择。

对于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 

确定是否有超线程处于关闭状态,超线程关闭或没有超线程是不可靠的方法。

相反,更好的方法是在第一次运行(或每次)时进行第一次校准,该第一次运行第一次测试,确定使用哪种方法。

另一种方法是使用所有处理器,即使超线程没有帮助(假设它不会使代码显着变慢)

几乎没有思考:

  • 超线程每个代码可能有2个以上的线程(Sparc可以有8个)
  • 垃圾收集器也需要CPU时间才能工作。
  • 超线程可以帮助并发GC – 或者可能不会; 或者JVM可以请求核心的独占(非超线程)所有者。 因此,从长远来看,阻碍GC在测试期间获得更好的结果可能会受到影响。
  • 如果存在高速缓存未命中,则超线程通常很有用,因此CPU不会停止,而是切换到另一个任务。 因此,“超线程与否”将取决于工作负载和CPU L1 / L2高速缓存大小/内存速度等。
  • 操作系统可能偏向于/反对某些线程,并且Thread.setPriority可能不受尊重(在Linux上它通常不受尊重)。
  • 可以设置流程的亲和力,禁止某些核心。 所以知道有超线程在这种情况下不会有任何重要的美德。

话虽这么说:你应该有一个工作线程大小的设置,并建议如何设置给定的架构细节。

没有办法从纯Java中确定(毕竟逻辑核心是核心,如果它是否使用HT实现)。 请注意,到目前为止提出的解决方案可以解决您的要求(正如您所问),但不仅英特尔CPU提供了超线程的forms(Sparc浮现在脑海中,我确信还有其他的)。

您也没有考虑到即使您确定系统使用HT,您也无法控制与Java核心的线程关联。 所以你仍然受操作系统的线程调度程序的支配。 虽然有可能的情况,较少的线程可以更好地执行(因为减少了缓存垃圾),但是没有办法静态地确定应该使用多少线程(在所有CPU具有非常不同的缓存大小之后(低端的256KB范围)现在可以合理地预期在服务器中大于16MB。并且每个新一代都必然会发生变化。

只需将其设置为可配置的设置,任何在不完全了解目标系统的情况下确定这一点的尝试都是徒劳的。

没有办法做到这一点,你可以做的一件事是创建Runtime.getRuntime().availableProcessors()的线程池Runtime.getRuntime().availableProcessors()应用程序中的线程,并在请求进入时使用。

这样你可以得到0 – Runtime.getRuntime().availableProcessors()线程数。

您可能无法可靠地查询操作系统或运行时,但您可以运行快速基准测试。

逐步增加自旋锁线程,测试每个新线程是否与前一个迭代。 一旦其中一个线程的性能低于之前测试的大约一半(至少对于intel,我不知道SPARC),你知道你已经开始与超线程共享一个核心。