我正在尝试使用Java(Sun JDK 1.7 64位)进行多重踩踏,试图更好地掌握一些概念。
我感到困惑的是找到执行程序的线程池大小以及该设置对性能的影响。这是我的基本代码:
public class Program {
static int bestThreads = 0;
static long bestTime = Integer.MAX_VALUE;
public static void main(String[] args) throws InterruptedException, ExecutionException {
int cores = Runtime.getRuntime().availableProcessors();
for (int sizeOfPool = 1; sizeOfPool <= cores; sizeOfPool++) {
ExecutorService exec = Executors.newFixedThreadPool(sizeOfPool);
//System.out.println("Started");
int noOftasks = 1000;
for (int i = 0; i < noOftasks; i++) {
Calculator c = new Calculator();
exec.submit(c);
}
long start = System.currentTimeMillis();
exec.shutdown();
exec.awaitTermination(1000, TimeUnit.DAYS);
long stop = (System.currentTimeMillis() - start);
//System.out.println("Done " + noOftasks + " tasks in " + stop + " on " + sizeOfPool + " threads");
if (bestTime > stop) {
bestTime = stop;
bestThreads = sizeOfPool;
}
}
System.out.println("Best size of pool " + bestThreads + " result in " + bestTime + " ms");
}
public static class Calculator implements Runnable {
@Override
public void run() {
doJob();
}
}
//Can be whatever this just gives me a few milliseconds worth of CPU load since I don't want to use Thread.sleep()
public static void doJob() {
for (int j = 0; j < 1E3; j++) {
Math.round(Math.sin(Math.sqrt(Math.random())));
}
}
当我运行该程序时,我发现使用最少时间的设置是使用N个线程的设置,其中N通常为2(这意味着我应该使用2个线程作为我的线程池的大小)。
我不明白为什么会这样,因为我从.availableProcessors()获得的处理器数量是4(我正在将i3与多线程一起使用,它在笔记本电脑上,Windows显示运行该程序时所有线程都处于 Activity 状态)。
当更改工作量时,我通常也会得到不同的结果:
1E1-> N = 4
1E2-> N = 3或2
1E3-> N = 2
1E4-> N = 2
但是即使那样,在大多数情况下,我还是得到N = 2;
有人可以解释一下为什么我会得到这样的结果,以及通常建议的池大小取决于程序在其上运行的CPU的大小。
这是我觉得奇怪的更多输出:
在1个线程中完成195个任务中的1000个任务//好吧,这个处理器大约需要200毫秒才能完成此任务,超频将在这里起到帮助作用
在2个线程上完成134个任务中的1000个任务///我知道由于上下文切换和线程创建开销的其他一些影响,我无法获得2倍的增长,但这是一个不错的加速
在3个线程上完成138个任务中的1000个任务//与2个线程几乎相同,为什么它并不更差或更好
在4个线程上完成了210个任务中的1000个任务///然后是1个线程,这是我真的没有得到的任务
最佳答案
您的“测试”作业完全受CPU限制,这意味着它仅取决于CPU/内核速度。尽管i3声称拥有4个内核,但它是一个双核CPU(2个内核,每个内核有2个线程-aka超线程)。
超线程无法为您提供4个完整的内核,每个内核都可以在其两个线程中的任何一个上工作(它会自动切换,例如在线程等待内存访问时自动切换)。因此,在您的测试用例中,i3 CPU的两个线程表现最佳,因为这是您的CPU可以(真正)同时处理的最大数量。
使用不同的测试(例如,具有大量的内存访问或等待I/O),您将获得不同的“理想”线程号。
编辑:我不知道在Java中区分真正的“物理”核心和“虚拟”核心的方法。在这方面,较新的AMD CPU有其独特之处(独立的内核,但FPU在2个内核之间共享),因此其技术水平非常低。要真正获得所有详细信息,您可能需要读取CPU-Id并检查该CPU的数据表。
之所以会出现2,有时是3的原因,可能是由于多线程测试不是真正确定性的(操作系统不可避免地会在随机时间吃掉一些CPU)。另外,由于JIT预热,短期测试显示Java中通常会有很多变化(寻找微基准测试,这是一个复杂的话题)。
无论如何,您都应该看到i3/i7之间的区别。