【问题标题】:Why adding async boundary in Akka Streams costs a lot of CPU?为什么在 Akka Streams 中添加异步边界会消耗大量 CPU?
【发布时间】:2018-10-19 05:01:54
【问题描述】:

我发现我的 Akka Streams 程序有意外的 CPU 使用率。

这是一个简单的例子:

import akka.actor.ActorSystem
import akka.stream.ActorMaterializer
import akka.stream.scaladsl.{Sink, Source}

implicit val system: ActorSystem = ActorSystem.create("QuickStart")
implicit val materializer: ActorMaterializer = ActorMaterializer()

Source.repeat(Unit)
  .to(Sink.ignore)
  .run()

上面的代码片段将让 source 和 sink 在同一个 actor 中运行。

它在我的笔记本电脑上使用了大约 105% 的 CPU 使用率。按预期工作。

在我添加了一个异步边界之后:

import akka.actor.ActorSystem
import akka.stream.ActorMaterializer
import akka.stream.scaladsl.{Sink, Source}

implicit val system: ActorSystem = ActorSystem.create("QuickStart")
implicit val materializer: ActorMaterializer = ActorMaterializer()

Source.repeat(Unit)
  .async // <------ async boundary here
  .to(Sink.ignore)
  .run()

这段代码现在将使用我 4c8t 笔记本电脑上大约 600% 的 CPU 使用率。

我期望通过添加异步边界,此流将在 2 个单独的参与者中运行,并且将花费 200% 以上的 CPU。但它的成本远远超过 200%。

什么可能导致异步边界使用这么多 CPU?

【问题讨论】:

  • 你可以尝试分析来找出(或者只是在shell上使用jstack &lt;pid&gt;。最可能的情况是你的线程池太大了。尝试设置akka.actor.default-dispatcher.fork-join-executor.parallelism-factor = 1。

标签: scala akka akka-stream


【解决方案1】:

默认akka.actor.default-dispatcher 参数是Java 的ForkJoinPool。它通过调用ThreadPoolConfig.scaledPoolSize 进行初始化。因此它默认为大小的起始池(处理器数 * 3)和 max = parallelism-max (64)。

【讨论】:

  • 感谢您的回答!我尝试更改 akka.actor.default-dispatcher.fork-join-executor.parallelism-max,并成功限制了线程数。这按预期工作。但我的问题是,是什么让 Akka 流能够消耗超过 200% 的 CPU?因为在我的理解中,添加异步边界后会有两个参与者/线程处于活动状态。因此,即使有更多线程,CPU 使用率也应该在 200% 左右。
  • 我认为默认设置允许 .async 在您的 4 个 cpu 内核上分配线程,从而使 CPU 负载 > 100%。虽然我不知道为什么不是 400%。
  • 是的,设置一个异步边界将让它尽可能地扩展:没有两个线程或两个参与者的限制。 (由于您可能有两个以上的传入事件,因此它将扩展到两个以上的参与者。)调度程序中的确切线程数将取决于您的并行度因素和您的硬件。
猜你喜欢
  • 2022-11-18
  • 1970-01-01
  • 2012-11-16
  • 1970-01-01
  • 2016-06-08
  • 1970-01-01
  • 2011-05-10
  • 1970-01-01
  • 2019-08-16
相关资源
最近更新 更多