gpt4 book ai didi

java - ParallelStream 上的 CompletableFuture 被批处理并且运行速度比顺序流慢?

转载 作者:行者123 更新时间:2023-12-03 17:17:47 27 4
gpt4 key购买 nike

方法一
通常,非常快,并且效果很好。

public static int loops = 500;
private static ExecutorService customPool = Executors.newFixedThreadPool(loops);
.
.
Instant start = Instant.now();
LongSummaryStatistics stats = LongStream.range(0, loops).boxed()
.map(number -> CompletableFuture.supplyAsync(() -> DummyProcess.slowNetworkCall(number), customPool))
.collect(Collectors.toList()).stream() // collect first, else will be sequential
.map(CompletableFuture::join)
.mapToLong(Long::longValue)
.summaryStatistics();

log.info("cf completed in :: {}, summaryStats :: {} ", Duration.between(start, Instant.now()).toMillis(), stats);
// ... cf completed in :: 1054, summaryStats :: LongSummaryStatistics{count=500, sum=504008, min=1000, average=1008.016000, max=1017}
我明白,如果我不先收集流,那么由于懒惰的性质,流会一个一个地弹出CompletableFutures,并同步运行。所以,作为一个实验:
方法二
删除中间收集步骤, 但也要使流平行! :
Instant start = Instant.now();
LongSummaryStatistics stats = LongStream.range(0, loops).boxed()
.parallel()
.map(number -> CompletableFuture.supplyAsync(() -> DummyProcess.slowNetworkCall(number), customPool))
.map(CompletableFuture::join) // direct join
.mapToLong(Long::longValue).summaryStatistics();

log.info("cfps_directJoin completed in :: {}, summaryStats :: {} ", Duration.between(start, Instant.now()).toMillis(), stats);
// ... cfps_directJoin completed in :: 8098, summaryStats :: LongSummaryStatistics{count=500, sum=505002, min=1000, average=1010.004000, max=1015}
摘要:
  • 方法 1:: 1 秒
  • 方法 2:: 8 秒

  • 我观察到的模式:
  • 并行流方法一次“批处理”60 个调用,因此有 500 个循环,500/60 ~ 8 个批处理,每个需要 1 秒,因此总共 8
  • 所以,当我将循环计数减少到 300 时,有 300/60 = 5 个批次,实际完成需要 5 秒。

  • 所以,问题是:
    为什么在并行+直接收集方法中会有这种批处理调用?

    为了完成,这是我的虚拟网络调用方法:
        public static Long slowNetworkCall(Long i) {
    Instant start = Instant.now();
    log.info(" {} going to sleep..", i);
    try {
    TimeUnit.MILLISECONDS.sleep(1000); // 1 second
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    log.info(" {} woke up..", i);
    return Duration.between(start, Instant.now()).toMillis();
    }

    最佳答案

    这是一个 Artifact 如何ForJoinPool当你阻塞它的内部线程时处理事情,以及它产生多少新线程。虽然,我可能会找到发生这种情况的确切线路,但我不确定这是否值得。有两个原因:

  • 这种逻辑可以改变
  • 里面的代码ForkJoinPool远非微不足道

  • 似乎对我们俩来说, ForkJoinPool.commonPool().getParallelism()将返回 11 ,所以我得到和你一样的结果。如果您登录 ForkJoinPool.commonPool().getPoolSize()要了解您的代码使用了多少 Activity 线程,您会看到一段时间后,它只是稳定在 64 处。 .所以可以同时处理的最大任务是 64 ,这与您看到的结果(那些 8 seconds )相当。
    如果我用 -Djava.util.concurrent.ForkJoinPool.common.parallelism=50 运行你的代码,现在在 2 seconds 中执行,池大小增加到 256 .这意味着,有一个内部逻辑可以调整这些事情。

    关于java - ParallelStream 上的 CompletableFuture 被批处理并且运行速度比顺序流慢?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/67411386/

    27 4 0