2011-03-04 183 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