簡體   English   中英

意外的可伸縮性導致Java Fork-Join(Java 8)

[英]Unexpected Scalability results in Java Fork-Join (Java 8)

最近,我正在使用Java Fork-Join進行一些可伸縮性實驗。 在這里,我使用了非默認的ForkJoinPool構造函數ForkJoinPool(int parallelism) ,將所需的並行性(#worker)作為構造函數參數傳遞。

具體來說,使用以下代碼:

public static void main(String[] args) throws InterruptedException {
    ForkJoinPool pool = new ForkJoinPool(Integer.parseInt(args[0]));
    pool.invoke(new ParallelLoopTask());    
}

static class ParallelLoopTask extends RecursiveAction {

    final int n = 1000;

    @Override
    protected void compute() {
        RecursiveAction[] T = new RecursiveAction[n];
        for(int p = 0; p < n; p++){
            T[p] = new DummyTask();
            T[p].fork();
        }
        for(int p = 0; p < n; p++){
            T[p].join();
        }
        /*
        //The problem does not occur when tasks are joined in the reverse order, i.e.
        for(int p = n-1; p >= 0; p--){
            T[p].join();
        }
        */
    }
}


static public class DummyTask extends RecursiveAction {
    //performs some dummy work

    final int N = 10000000;

    //avoid memory bus contention by restricting access to cache (which is distributed)
    double val = 1;

    @Override
    protected void compute() {
        for(int j = 0; j < N; j++){
            if(val < 11){
                val *= 1.1;
            }else{
                val = 1;
            }
        }
    }
}

我在具有4個物理和8個邏輯內核的處理器上獲得了這些結果(使用Java 8:jre1.8.0_45):

T1:11730

T2:2381(加速:4,93)

T4:2463(加速:4,76)

T8:2418(加速:4,85)

當使用Java 7(jre1.7.0)時,我得到

T1:11938

T2:11843(加速:1,01)

T4:5133(加速:2,33)

T8:2607(加速:4,58)

(其中TP是執行時間,以毫秒為單位,使用並行度P)

雖然這兩個結果都令我感到驚訝,但我可以理解后者(聯接將導致1個工作程序(執行循環)被阻塞,因為它無法識別它可以在等待時處理其本地隊列中的其他未完成的虛擬任務)。 但是,前者讓我感到困惑。

順便說一句:當計算開始但尚未完成的虛擬任務的數量時,我發現某個時間點內並行度為2的池中最多存在24個此類任務...?

編輯:

我使用JMH(jdk1.8.0_45)(選項-bm avgt -f 1)(= 1 fork,20 + 20次迭代)對上述應用程序進行了基准測試

T1:11,664

11,664 ±(99.9%) 0,044 s/op [Average]
(min, avg, max) = (11,597, 11,664, 11,810), stdev = 0,050
CI (99.9%): [11,620, 11,708] (assumes normal distribution)

T2:4,134(加速:2,82)

4,134 ±(99.9%) 0,787 s/op [Average]
(min, avg, max) = (3,045, 4,134, 5,376), stdev = 0,906
CI (99.9%): [3,348, 4,921] (assumes normal distribution)

T4:2,972(加速:3,92)

2,972 ±(99.9%) 0,212 s/op [Average]
(min, avg, max) = (2,375, 2,972, 3,200), stdev = 0,245
CI (99.9%): [2,759, 3,184] (assumes normal distribution)

T8:2,845(加速比:4,10)

2,845 ±(99.9%) 0,306 s/op [Average]
(min, avg, max) = (2,277, 2,845, 3,310), stdev = 0,352
CI (99.9%): [2,540, 3,151] (assumes normal distribution)

乍一看,人們會認為這些可伸縮性結果更接近人們的預期,即T1 <T2 <T4〜T8。 但是,仍然困擾我的是以下內容:

  1. Java 7和8之間T2的區別。我猜一個解釋是,執行並行循環的工作程序不會在Java 8中變為空閑狀態,而是找到其他要執行的工作。
  2. 2名工人的超線性加速(3倍)。 另外,請注意,T2似乎隨着每次迭代都增加(請參見下文,請注意,盡管P = 4,8的程度較小,情況也是如此)。 預熱的第一次迭代中的時間與上面提到的時間相似。 也許預熱時間應該更長一些,但是執行時間增加並不奇怪,例如,我希望它減少嗎?
  3. 最后,我仍然發現,啟動和未完成的虛擬任務比工作線程好奇的多。

>

Run progress: 0,00% complete, ETA 00:00:40
Fork: 1 of 1
Warmup Iteration   1: 2,365 s/op
Warmup Iteration   2: 2,341 s/op
Warmup Iteration   3: 2,393 s/op
Warmup Iteration   4: 2,323 s/op
Warmup Iteration   5: 2,925 s/op
Warmup Iteration   6: 3,040 s/op
Warmup Iteration   7: 2,304 s/op
Warmup Iteration   8: 2,347 s/op
Warmup Iteration   9: 2,939 s/op
Warmup Iteration  10: 3,083 s/op
Warmup Iteration  11: 3,004 s/op
Warmup Iteration  12: 2,327 s/op
Warmup Iteration  13: 3,083 s/op
Warmup Iteration  14: 3,229 s/op
Warmup Iteration  15: 3,076 s/op
Warmup Iteration  16: 2,325 s/op
Warmup Iteration  17: 2,993 s/op
Warmup Iteration  18: 3,112 s/op
Warmup Iteration  19: 3,074 s/op
Warmup Iteration  20: 2,354 s/op
Iteration   1: 3,045 s/op
Iteration   2: 3,094 s/op
Iteration   3: 3,113 s/op
Iteration   4: 3,057 s/op
Iteration   5: 3,050 s/op
Iteration   6: 3,106 s/op
Iteration   7: 3,080 s/op
Iteration   8: 3,370 s/op
Iteration   9: 4,482 s/op
Iteration  10: 4,325 s/op
Iteration  11: 5,002 s/op
Iteration  12: 4,980 s/op
Iteration  13: 5,121 s/op
Iteration  14: 4,310 s/op
Iteration  15: 5,146 s/op
Iteration  16: 5,376 s/op
Iteration  17: 4,810 s/op
Iteration  18: 4,320 s/op
Iteration  19: 5,249 s/op
Iteration  20: 4,654 s/op

在您如何進行此基准測試的示例中沒有任何內容。 看起來您在運行的開始和結束都只是做了一分鍾。 這是不准確的。 我建議您看看這個SO答案,然后重新發布您的時間安排。 順便說一句,jmh基准測試將成為Java9中的標准,因此您應該使用它。

編輯:

您承認可伸縮性結果符合您的預期。 但是您說您仍然對結果不滿意。 現在該看一下代碼內部了。

該框架存在嚴重問題。 自2010年以來,我一直在對此進行評論。正如我在這里指出的那樣,join不起作用。 作者嘗試了各種方法來解決該問題,但問題仍然存在。

將您的運行時間增加到一分鍾左右(n = 100000000)或在compute()中進行大量計算。 現在在VisualVM或其他探查器中探查應用程序。 這將向您顯示停滯線程,過多線程等。

如果那不能幫助您回答問題,則應使用調試器查看代碼流。 分析/代碼分析是您獲得滿意答案的唯一途徑。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM