我想知道我可以运行的最佳线程数。通常,这等于Runtime.getRuntime().availableProcessors()
。
但是,在支持超线程的CPU上,返回的数字是两倍。现在,对于某些任务,超线程是好的,但对于其他任务,它什么都不做。在我的情况下,我怀疑,它什么也没做,所以我想知道我是否必须将Runtime.getRuntime().availableProcessors()
返回的数字除以两个。
为此,我必须推断CPU是否是超线程。因此我的问题 - 我怎么能用Java做呢?
感谢。
修改
好的,我已对我的代码进行了基准测试。这是我的环境:
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个工作线程似乎是此任务和我的机器的正确选择。
答案 0 :(得分:5)
不幸的是,这不可能来自java。如果您知道该应用程序将在现代Linux变体上运行,您可以读取文件/ proc / cpuinfo并推断是否启用了HT。
读取此命令的输出可以解决问题:
grep -i "physical id" /proc/cpuinfo | sort -u | wc -l
答案 1 :(得分:4)
确定您是否启用了超线程,关闭超线程还是没有超线程,这是不可靠的方法。
相反,更好的方法是在第一次运行时(或每次运行)进行第一次校准,该第一次运行第一次测试,确定使用哪种方法。
另一种方法是使用所有处理器,即使超线程没有帮助(只要它不会使代码显着变慢)
答案 2 :(得分:2)
更多的沉思:
话虽如此:你应该有一个工作线程大小的设置,并建议如何根据架构的具体情况进行设置。
答案 3 :(得分:1)
无法从纯Java中确定(毕竟逻辑核心是核心,如果它是使用HT实现的话)。请注意,到目前为止提出的解决方案可以解决您的要求(正如您所问),但不仅英特尔CPU提供了超线程的形式(Sparc浮现在脑海中,我确信还有其他的)。
您也没有考虑到即使 if 确定系统使用HT,您也无法控制与Java核心的线程关联。所以你仍然受操作系统的线程调度程序的支配。虽然有可能的情况,较少的线程可以更好地执行(因为减少了缓存垃圾),但是没有办法静态地确定应该使用多少线程(在所有CPU确实具有非常不同的缓存大小之后(a)从低端的256KB到服务器中的> 16MB,现在可以合理地预期。而且每一代都有新的变化。
只需将其设置为可配置的设置,任何在不完全了解目标系统的情况下确定这一点的尝试都是徒劳的。
答案 4 :(得分:1)
对于Windows
,如果逻辑核心数大于核心数,则您已启用hyper-threading
。进一步了解here。
您可以使用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();
}
}
答案 5 :(得分:0)
没有办法做到这一点,您可以做的一件事是在您的应用程序中创建Runtime.getRuntime().availableProcessors()
线程的线程池,并在请求进入时使用。
这样你就可以有0 - Runtime.getRuntime().availableProcessors()
个线程。
答案 6 :(得分:0)
您可能无法可靠地查询操作系统或运行时,但您可以运行快速基准测试。
逐步增加自旋锁线程,测试每个新线程是否与前一个迭代。一旦其中一个线程的性能低于之前测试的大约一半(至少对于intel,我不知道SPARC),你知道你已经开始与超线程共享一个核心。