使用 RandomStringUtils 模拟数据时,Java 并行流比串行慢

问题描述 投票:0回答:1

我有大约 1000000 个段落的数据库记录,每个段落大约有 500 个字符。通过阅读所有记录,我需要获取按使用最多到最少使用的顺序排列的字母表列表。

我通过创建高达 1000000 的流来模拟数据库读取,然后并行处理该流

final Map<Character, Long> charCountMap = new ConcurrentHashMap<>();
for (char c = 'a'; c <= 'z'; c++) {
    charCountMap.put(c, 0l);
}

System.out.println("Parallel Stream");
long start = System.currentTimeMillis();
Stream.iterate(0, i -> i).limit(1000000).parallel() //mock database stream
    .forEach(i-> RandomStringUtils.randomAlphanumeric(500)
    .toLowerCase().chars().mapToObj(c -> Character.valueOf((char) c)).filter(c -> c >= 97 && c <= 122)
    .forEach(c -> charCountMap.compute(c, (k, v) -> v + 1))); //update ConcurrentHashMap

long end = System.currentTimeMillis();
System.out.println("Parallel Stream time spent :" + (end - start));

System.out.println("Serial Stream"); start = System.currentTimeMillis();
Stream.iterate(0, i -> i).limit(1000000) //mock database stream
    .forEach(i-> RandomStringUtils.randomAlphanumeric(500)
    .toLowerCase().chars().mapToObj(c -> Character.valueOf((char) c)).filter(c -> c >= 97 && c <= 122)
    .forEach(c -> charCountMap.compute(c, (k, v) -> v + 1)));
end = System.currentTimeMillis();
System.out.println("Serial Stream time spent :" + (end - start));

我最初认为即使流的预期开销大于 100,000,并行流也会更快。然而,测试表明,即使对于 1,000,000 条记录,串行流也比并行流快约 5 倍。

我怀疑是因为更新了

ConcurrentHashMap
。但是当我删除它并用空函数更改时,仍然存在显着的性能差距。

我的数据库模拟调用或我使用并行流的方式有问题吗?

java concurrency parallel-processing java-stream apache-commons-lang3
1个回答
0
投票

使用

RandomStringUtils.randomAlphanumeric(500)
不适合与
parallel()
一起使用,因为根据此处的代码它使用静态变量来生成随机字符串。因此,所有线程生成随机字符串的所有调用都将在
Random
的同一底层实例上发生争用:

private static final Random RANDOM = new Random();

编写自己的随机字符串生成器,每个线程使用

Random
的单个实例或使用 java.util.concurrent.ThreadLocalRandom - 这可以避免随机序列的争用。在将其编辑为使用 ThreadLocalRandom 之前,同样的问题会导致该问题性能不佳。

参见 javadoc

java.util.Random 说:

Instances of java.util.Random are threadsafe. However, the concurrent use of the same java.util.Random instance across threads may encounter contention and consequent poor performance. Consider instead using java.util.concurrent.ThreadLocalRandom in multithreaded designs.
    
© www.soinside.com 2019 - 2024. All rights reserved.