2

新しい Java ストリームをいじっているうちに、並列ストリームのパフォーマンスに関連する奇妙なことに気付きました。テキスト ファイルから単語を読み取り、長さが 5 を超える単語をカウントする単純なプログラムを使用しました (テスト ファイルには 30000 単語があります)。

    String contents = new String(Files.readAllBytes(Paths.get("text.txt")));
    List<String> words = Arrays.asList(contents.split("[\\P{L}]+"));
    long startTime;
    for (int i = 0; i < 100; i++) {
        startTime = System.nanoTime();
        words.parallelStream().filter(w -> w.length() > 5).count();
        System.out.println("Time elapsed [PAR]: " + (System.nanoTime() - startTime));
        startTime = System.nanoTime();
        words.stream().filter(w -> w.length() > 5).count();
        System.out.println("Time elapsed [SEQ]: " + (System.nanoTime() - startTime));
        System.out.println("------------------");
    }

これにより、私のマシンで次の出力が生成されます (最初と最後の 5 回のループ反復についてのみ言及します)。

Time elapsed [PAR]: 114185196
Time elapsed [SEQ]: 3222664
------------------
Time elapsed [PAR]: 569611
Time elapsed [SEQ]: 797113
------------------
Time elapsed [PAR]: 678231
Time elapsed [SEQ]: 414807
------------------
Time elapsed [PAR]: 755633
Time elapsed [SEQ]: 679085
------------------
Time elapsed [PAR]: 755633
Time elapsed [SEQ]: 393425
------------------
...
Time elapsed [PAR]: 90232
Time elapsed [SEQ]: 163785
------------------
Time elapsed [PAR]: 80396
Time elapsed [SEQ]: 154805
------------------
Time elapsed [PAR]: 83817
Time elapsed [SEQ]: 154377
------------------
Time elapsed [PAR]: 81679
Time elapsed [SEQ]: 186449
------------------
Time elapsed [PAR]: 68849
Time elapsed [SEQ]: 154804
------------------

最初の処理が他の処理よりも 100 倍遅いのはなぜですか? 最初の反復では並列ストリームが順次ストリームよりも遅いのに、最後の反復では 2 倍の速度になるのはなぜですか? シーケンシャル ストリームとパラレル ストリームの両方が時間の経過とともに高速になるのはなぜですか? これはループの最適化に関連していますか?

後で編集: Luigi の提案で、JUnitBenchmarksを使用してベンチマークを実装しました。

List<String> words = null;

@Before
public void setup() {
    try {
        String contents = new String(Files.readAllBytes(Paths.get("text.txt")));
        words = Arrays.asList(contents.split("[\\P{L}]+"));
    } catch (IOException e) {
        e.printStackTrace();
    }
}

@BenchmarkOptions(benchmarkRounds = 100)
@Test
public void parallelTest() {
    words.parallelStream().filter(w -> w.length() > 5).count();
}

@BenchmarkOptions(benchmarkRounds = 100)
@Test
public void sequentialTest() {
    words.stream().filter(w -> w.length() > 5).count();
}

また、テスト ファイルの単語数を 300000 に増やしました。新しい結果は次のとおりです。

Benchmark.sequentialTest: [105 ラウンド中 100 ラウンドを測定、スレッド: 1 (連続)]

round: 0.08 [+- 0.04]、round.block: 0.00 [+- 0.00]、round.gc: 0.00 [+- 0.00]、GC.calls: 62、GC.time: 1.53、time.total: 8.65、時間.ウォームアップ: 0.81、タイム.ベンチ: 7.85

Benchmark.parallelTest: [105 ラウンド中 100 ラウンドを測定、スレッド: 1 (順次)]

round: 0.06 [+- 0.02]、round.block: 0.00 [+- 0.00]、round.gc: 0.00 [+- 0.00]、GC.calls: 32、GC.time: 0.79、time.total: 6.82、時間.ウォームアップ: 0.39、タイム.ベンチ: 6.43

したがって、最初の結果は、間違ったマイクロベンチマーク構成が原因だったようです...

4

1 に答える 1

2

Hotspot JVM はインタープリター モードでプログラムの実行を開始し、分析後に頻繁に使用される部分をネイティブ コードにコンパイルします。このため、ループの最初の繰り返しは一般的に遅くなります。

于 2014-04-10T21:26:27.843 に答える