是否可以在Java中检查CPU是否为超线程?
我想知道我可以运行的线程的最佳数量。通常,这等于是否可以在Java中检查CPU是否为超线程?,java,multithreading,hyperthreading,Java,Multithreading,Hyperthreading,我想知道我可以运行的线程的最佳数量。通常,这等于Runtime.getRuntime().availableProcessors() 但是,在支持超线程的CPU上,返回的数字是原来的两倍。现在,对于某些任务来说,超线程是好的,但对于其他任务来说,它什么也做不了。在我的情况下,我怀疑它没有任何作用,因此我想知道是否必须将Runtime.getRuntime().availableProcessors()返回的数字一分为二 为此,我必须推断CPU是否为超线程。因此我的问题是——我如何用Java实现它
Runtime.getRuntime().availableProcessors()
但是,在支持超线程的CPU上,返回的数字是原来的两倍。现在,对于某些任务来说,超线程是好的,但对于其他任务来说,它什么也做不了。在我的情况下,我怀疑它没有任何作用,因此我想知道是否必须将Runtime.getRuntime().availableProcessors()
返回的数字一分为二
为此,我必须推断CPU是否为超线程。因此我的问题是——我如何用Java实现它
谢谢
编辑
好的,我已经对我的代码进行了基准测试。这是我的环境:
- 联想ThinkPad W510(即4核i7 CPU和超线程),16G内存
- 视窗7
- 84个压缩的CSV文件,压缩大小从105M到16M不等
- 所有文件都在主线程中逐个读取-没有对HD的多线程访问
- 每个CSV文件行都包含一些数据,这些数据将被解析,快速上下文无关测试将确定该行是否相关
- 每个相关行包含两个双精度(出于好奇,表示经度和纬度),它们被强制转换为一个
,然后存储在共享哈希集中Long
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.
}
}
}
你可以自由得出自己的结论,但我的结论是,在我的具体案例中,超读确实会提高性能。另外,对于这个任务和我的机器来说,拥有6个工作线程似乎是正确的选择 不幸的是,这在java中是不可能的。如果您知道该应用程序将在现代linux变体上运行,则可以读取文件/proc/cpuinfo并推断HT是否已启用 读取此命令的输出可以实现以下目的:
grep -i "physical id" /proc/cpuinfo | sort -u | wc -l
没有办法做到这一点,您可以做的一件事是在应用程序中创建
Runtime.getRuntime().availableProcessors()线程的线程池,并在请求传入时使用
通过这种方式,您可以拥有0-运行时.getRuntime().availableProcessors()
线程数。无法可靠地确定是否启用了超线程、禁用了超线程或没有超线程
相反,更好的方法是在您第一次运行(或每次运行)时进行第一次校准,运行第一次测试,确定使用哪种方法
另一种方法是使用所有处理器,即使“超线程”没有帮助(前提是它不会使代码显著变慢)无法从纯Java中确定这一点(毕竟,逻辑核心是核心,如果它使用HT实现或不使用HT实现)。请注意,目前提出的解决方案可以解决您的需求(如您所要求的),但不仅仅是Intel CPU提供了一种形式的超线程(我想到的是Sparc,我相信还有其他解决方案)
您还没有考虑到,即使您确定系统使用HT,您也无法控制线程与Java内核的关联性。因此,您仍然受操作系统线程调度程序的支配。虽然存在一些可能的情况,即线程越少,性能越好(因为缓存破坏越少),但无法静态地确定应该使用多少线程(毕竟所有CPU的缓存大小都非常不同)(从低端的256KB到服务器的>16MB的范围在今天是可以合理预期的。每一代服务器都会发生变化)
只需将其设置为可配置设置,任何在不确切了解目标系统的情况下确定此设置的尝试都是徒劳的。更多思考:
- 超线程可能每个代码有2个以上的线程(Sparc可以有8个)
- 垃圾收集器也需要CPU时间来工作
- 超线程可能有助于并发GC,也可能没有;或者JVM可能会请求成为核心的独占(而不是超线程)所有者。因此,从长远来看,妨碍GC在测试期间获得更好的结果可能是有害的
- 如果存在缓存未命中,超读通常很有用,因此CPU不会暂停,而是切换到另一个任务。因此,“是否超读”将取决于工作负载和CPU一级/二级缓存大小/内存速度等
- 操作系统可能对某些线程有偏见,Thread.setPriority可能不受尊重(在Linux上通常不受尊重)
- 可以设置进程的亲和力,不允许使用某些内核。因此,在这种情况下,知道存在超线程并没有任何显著的优点
这就是说:您应该设置工作线程的大小,并根据体系结构的具体情况建议如何设置。您可能无法可靠地查询操作系统或运行时,但可以运行快速基准测试
逐步增加自旋锁线程,测试每个新线程是否与前一个线程一样迭代。一旦其中一个线程的性能低于前一个测试的一半左右(至少对于intel,我不知道SPARC),您知道您已开始与超线程共享一个内核。对于Windows
,如果逻辑内核的数量高于内核的数量,则表示您已启用。请阅读有关它的详细信息
您可以使用wmic
查找以下信息:
C:\WINDOWS\system32>wmic CPU Get NumberOfCores,NumberOfLogicalProcessors /Format:List
NumberOfCores=4
NumberOfLogicalProcessors=8
因此,我的系统具有超线程
。逻辑处理器的数量是内核的两倍
但是您甚至可能不需要知道。Runtime.getRuntime().availableProcessor()
已经返回了逻辑处理器的数量
获取物理内核计数的完整示例(Windows
only):
有趣的问题
C:\WINDOWS\system32>wmic CPU Get NumberOfCores,NumberOfLogicalProcessors /Format:List
NumberOfCores=4
NumberOfLogicalProcessors=8
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();
}
}