Akka如何从ForkJoinPool中受益?

gab*_*ssi 9 java multithreading threadpool akka forkjoinpool

Akka docs表示默认调度程序是一个fork-join-executor因为它"在大多数情况下都能提供出色的性能".
我想知道为什么会这样?

来自ForkJoinPool

ForkJoinPool与其他类型的ExecutorService的不同之处主要在于使用工作窃取:池中的所有线程都尝试查找和执行提交给池的任务和/或由其他活动任务创建的任务(如果不存在则最终阻止等待工作) .这使得(1)在大多数任务产生其他子任务(如大多数ForkJoinTasks)时有效处理,以及(2)当许多小任务从外部客户端提交到池时.特别是在构造函数中将asyncMode设置为true时,ForkJoinPools也可以(3)适用于从未加入的事件样式任务.

起初,我猜Akka不是案例(1)的一个例子,因为我无法弄清楚Akka如何分配任务,我的意思是,在许多任务中可以分叉的任务是什么?
我认为每条消息都是一个独立的任务,这就是为什么我认为Akka与case(2)类似,其中消息是许多小任务(通过!和?)提交给ForkJoinPool.

下一个问题虽然与akka没有严格关系,但是,为什么ForkJoinPool fork_joinPool会使fork和join(允许工作窃取的主要功能)的用例仍然没有被使用?
Fork Join Pool的可扩展性

我们注意到上下文切换的数量异常,超过每秒70000.
那一定是问题所在,但究竟是什么原因造成的呢?Viktor提出了合格的猜测,它必须是线程池执行器的任务队列,因为它是共享的,并且LinkedBlockingQueue中的锁可能会在存在争用时生成上下文切换.

但是,如果Akka不使用ForkJoinTasks,则外部客户端提交的所有任务都将在共享队列中排队,因此争用应与in中的相同ThreadPoolExecutor.

所以,我的问题是:

  1. Akka使用ForkJoinTasks(案例(1))还是与案例(2)有关?
  2. ForkJoinPool如果外部客户端提交的所有任务都被推送到共享队列并且不会发生工作窃取,为什么在情况(2)中是有益的?
  3. 什么是"具有从未加入的事件风格任务"的例子(案例3)?

更新

正确答案来自johanandren,但我想补充一些亮点.

  • Akka不使用fork和join函数,因为AFAIK与Actor模型,或者至少我们如何实现它,没有真正的用例(来自johanandren的评论).
    所以我的理解是Akka不是案例(1)的实例是正确的.
  • 在我的原始答案中,我说外部客户端提交的所有任务都将在共享队列中排队.
    这是正确的,但仅适用于FJP的先前版本(jdk7).在jdk8中,单个sumbission队列被许多"提交队列"取代. 这个答案解释得很好:

    现在,在(IIRC)JDK 7u12之前,ForkJoinPool有一个全局提交队列.当工作线程用尽本地任务以及窃取任务时,他们到达那里并试图查看外部工作是否可用.在这种设计中,对于由ArrayBlockingQueue支持的常规ThreadPoolExecutor没有任何优势.[...]
    现在,外部提交进入其中一个提交队列.然后,没有工作的工作人员可以首先查看与特定工作者关联的提交队列,然后四处寻找其他人的提交队列.人们也可以称之为"偷工作".

因此,这可以在未使用fork join的情况下窃取工作.正如Doug Lea所说

当许多客户提交大量任务时,吞吐量大大提高.(我已经测量了高达60倍的speedupson微基准测试).我们的想法是以与工人类似的方式对待外部提交者 - 使用随机排队和转发.(这需要一个大的内部重构todisassociate工作队列和工作人员.)这也大大提高了所有任务异步并提交到池而不是分叉的吞吐量,这成为构建actor框架的合理途径,以及许多你可能使用的普通服务ThreadPoolExecutor for.

  • 还有一个值得一提的奇点是关于FJP的评论

    对于FJP来说,4%确实不多.你还需要注意FJP的权衡:FJP让线程旋转一段时间,以便能够更快地处理准时到达的工作.这确保了在许多情况下的良好延迟.特别是如果您的池过度配置,那么在几乎空闲的情况下,权衡时间与更多功耗有关.

joh*_*ren 6

Akka的FJP运行asyncMode = true于第一个问题,即-让外部客户端提交短/小的异步工作负载。每个提交的工作负载要么派遣一个actor处理其收件箱中的一条或几条消息,但它也用于执行Scala Future操作。

当非ForkJoinTask计划在FJP上运行时,它将适应FJP并像ForkJoinTasks 一样排队。没有一个单一的提交将任务排入队列(可能是早期版本的JDK7),有很多避免争用的方法,并且空闲线程可以从其他队列(窃取)中挑选(窃取)任务空的。

请注意,默认情况下,当前我们在Java 8 FJP的分支版本上运行,因为Java 9 FJP的吞吐量显着下降(它包含很多更改)。这里是问题#21910,讨论您是否有兴趣。此外,如果您想测试不同池的基准,则可以*Pool在此处找到一些基准:https : //github.com/akka/akka/tree/master/akka-bench-jmh/src/main/scala/akka/演员