2011-03-04 185 views
3

我正在处理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纳秒左右。这是为什么?如果我运行这个代码(20次重复),然后执行其他的事情(让我们说对前面的结果进行后处理,并准备进行另一次多线程操作),然后在同一个ThreadPool上执行同样的Runnable再进行20次重复,它会无论如何,已经被预热了?在我的程序中,我只在一个线程中执行Runnable(实际上每个处理核心有一个,它是一个CPU密集型程序),然后是其他一些串行处理交替进行多次。随着程序的执行,它似乎并没有变得更快。也许我可以找到一种方法来预热它...

回答

9

这不是像JVM那样热身的线程。

JVM具有所谓的JIT(Just In Time)编译。程序正在运行时,它会分析程序中发生的情况并在运行中对其进行优化。它通过获取JVM运行的字节码并将其转换为运行速度更快的本机代码来实现此目的。它可以通过分析实际的运行时行为,以最适合当前情况的方式实现此目的。这可以(并非总是)导致很大的优化。甚至比一些没有这些知识的编译为本地代码的程序更是如此。

您可以在http://en.wikipedia.org/wiki/Just-in-time_compilation

读多一点的代码加载到CPU缓存你可以得到任何程序类似的效果,但我相信这将是一个更小的差异。

+0

感谢您的解释,@ rfeak。但是你认为编译器能够优化我的程序吗? (请阅读我在问题中添加的最后一段) – ursoouindio 2011-03-04 20:17:15

+0

JIT编译器只能做这么多,只影响CPU时间。如果串行进程涉及任何类型的IO,编译器几乎无法完成。我会建议分析你的程序,看看时间花在哪里,然后攻击那里最大的瓶颈,如果你需要更多的性能。 – rfeak 2011-03-04 20:47:46

+0

实际上,它没有任何IO。我给出了初始条件,程序是由它自己运行的。我解决了一些特殊的微分方程。 – ursoouindio 2011-03-04 23:12:11

1

我看到一个执行线程可以结束了速度更快,唯一的理由是:

  • 内存管理器可以重新使用已分配的对象空间(例如,让堆分配填满可用内存,直到达到最大内存 - 在Xmx属性)

  • 工作集是在硬件缓存中可用

  • 重复该操作可能会造成操作的编译器ç更容易重新排序以优化执行

+0

这些原因与Java或其他语言无关吗? – ursoouindio 2011-03-04 20:15:12

+0

是的,没有。许多编程语言都重复使用对象空间进行重新分配,而“JIT”(即时编译)优化主要针对JVM/.NET语言。虽然Harware缓存对于所有平台都很常见。 – 2011-03-04 20:18:56