我正在处理Java中的多线程,正如有人向我指出的那样,我注意到线程正在热身,它们会在反复执行时变得更快。我想了解为什么会发生这种情况,以及它是否与Java本身有关,或者它是否是每个多线程程序的常见行为。
代码(由Peter Lawrey撰写)举例说明如下:
for (int i = 0; i < 20; i++) {
ExecutorService es = Executors.newFixedThreadPool(1);
final double[] d = new double[4 * 1024];
Arrays.fill(d, 1);
final double[] d2 = new double[4 * 1024];
es.submit(new Runnable() {
@Override
public void run() {
// nothing.
}
}).get();
long start = System.nanoTime();
es.submit(new Runnable() {
@Override
public void run() {
synchronized (d) {
System.arraycopy(d, 0, d2, 0, d.length);
}
}
});
es.shutdown();
es.awaitTermination(10, TimeUnit.SECONDS);
// get a the values in d2.
for (double x : d2) ;
long time = System.nanoTime() - start;
System.out.printf("Time to pass %,d doubles to another thread and back was %,d ns.%n", d.length, time);
}
结果:
Time to pass 4,096 doubles to another thread and back was 1,098,045 ns.
Time to pass 4,096 doubles to another thread and back was 171,949 ns.
... deleted ...
Time to pass 4,096 doubles to another thread and back was 50,566 ns.
Time to pass 4,096 doubles to another thread and back was 49,937 ns.
即。它变得更快并稳定在50 ns左右。那是为什么?
如果我运行此代码(20次重复),则执行其他操作(让我们说先前结果的后处理和另一个多线程轮次的准备),然后在同一Runnable
上执行相同的ThreadPool
对于另外20次重复,它会在任何情况下被预热吗?
在我的程序中,我只在一个线程中执行Runnable
(实际上每个处理核心一个,它是一个CPU密集型程序),然后交替执行其他一些串行处理多次。随着程序的推进,它似乎没有变得更快。也许我能找到一种方法来温暖它......
答案 0 :(得分:9)
线程不像JVM一样热身。
JVM具有所谓的JIT(即时)编译。在程序运行时,它会分析程序中发生的事情并在运行中对其进行优化。它通过获取JVM运行的字节代码并将其转换为运行速度更快的本机代码来实现此目的。它可以通过最适合您当前情况的方式执行此操作,因为它通过分析实际运行时行为来实现。这可以(并非总是)导致极大的优化。甚至比在没有这些知识的情况下编译为本机代码的某些程序更为重要。
您可以在http://en.wikipedia.org/wiki/Just-in-time_compilation
处阅读更多内容当代码加载到CPU缓存中时,您可能会对任何程序产生类似的影响,但我相信这会产生较小的差异。
答案 1 :(得分:1)
我发现线程执行最终可能更快的唯一原因是:
内存管理器可以重用已经分配的对象空间(例如,让堆分配填满可用内存,直到达到最大内存 - Xmx
属性)
工作集在硬件缓存中可用
重复操作可能会创建操作,编译器可以更轻松地重新排序以优化执行