Akka如何从ForkJoinPool中受益?

时间:2018-10-05 12:02:11

标签: java multithreading akka threadpool forkjoinpool

Akka docs指出默认调度程序是fork-join-executor,因为它在大多数情况下都具有出色的性能。
我想知道为什么吗?

来自ForkJoinPool

  

ForkJoinPool与其他类型的ExecutorService的不同之处在于,它采用了工作窃取:池中的所有线程都试图查找并执行提交给池和/或由其他活动任务创建的任务(最终,如果不存在)。这样,当大多数任务产生其他子任务时(大多数ForkJoinTasks也是如此),(1)高效处理;当许多小任务从外部客户端提交到池中时,(2)强>。尤其是在构造函数中将asyncMode设置为true时,ForkJoinPools也可能(3)适合与从未加入的事件样式任务一起使用。

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

尽管与akka并不严格相关,但下一个问题将是,为什么仍然可以使用不使用fork and join(允许进行偷窃的主要功能ForkJoinPool)的用例。受益于ForkJoinPool?
来自Scalability of Fork Join Pool

  

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

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

所以,我的问题是:

  1. Akka使用ForkJoinTasks(案例(1))还是与案例(2)相关?
  2. 如果外部客户提交的所有任务都将被推送到共享队列而没有任何窃取工作的情况(2),为什么ForkJoinPool有好处?
  3. “具有从未加入的事件样式任务”(案例3)的示例将是什么?

更新

正确的答案是johanandren的答案,但是我想添加一些要点。

  • Akka不使用fork和join功能,因为AFAIK具有Actor模型,或者至少我们是如何实现的,实际上并没有用例(摘自johanandren的评论)。 br /> 因此,我对Akka不是案例(1)的实例的理解是正确的。
  • 在我最初的回答中,我说外部客户提交的所有任务都将在共享队列中排队
    这是正确的,但仅适用于FJP的早期版本(jdk7)。 在jdk8中,single sumbission queue被许多“提交队列”所代替。 This answer很好地解释了这一点:
      

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

因此,这在不使用fork联接的情况下实现了工作窃取。以Doug Lea says

  

当许多客户端提交许多任务时,吞吐量显着提高。 (我测得的speedupson微基准测试最高可达60倍)。想法是像对待工人一样对待外部提交者-使用随机排队和窃取。 (这需要进行大量的内部重构以分离工作队列和工作程序。)当所有任务都异步并提交到池中而不是分叉时,这也极大地提高了吞吐量,这成为构造actor框架以及许多您可能会使用的纯服务的合理方法ThreadPoolExecutor用于。

  • 还有另外一个关于{@ 3}中的FJP的奇点
      

    FJP的4%确实不多。与FJP仍然需要权衡   您需要注意以下几点: FJP使线程保持旋转一段时间。   能够更快地处理准时到达的工作。这样可以确保良好的延迟   在很多情况下。但是,特别是如果您的池超额配置,   权衡是一点点的等待时间,几乎是闲置时的更多功耗   情况。

2 个答案:

答案 0 :(得分:3)

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

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

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

答案 1 :(得分:1)

http://letitcrash.com/post/17607272336/scalability-of-fork-join-pool

叉连接池的可伸缩性

由于Doug Lea开发了新的fork join执行程序,因此Akka 2.0消息传递吞吐量在多核硬件上的扩展方式比以前的版本更好。一个微型基准测试表明吞吐量提高了1100%!

...

http://cs.oswego.edu/pipermail/concurrency-interest/2012-January/008987.html

...

要点:

  1. 当有很多客户时吞吐量显着提高 提交很多任务。 (我测得的加速比高达60倍 在微基准测试上)。这个想法是对待外部提交者 以与工人类似的方式-使用随机排队和 偷。 (这需要进行大量的内部重构,以 取消工作队列和工人的联系。) 当所有任务都异步并提交时,提高了吞吐量 到游泳池而不是分叉,这变得合理 构造参与者框架的方法,以及许多简单的方法 您可能另外使用ThreadPoolExecutor的服务。

这些改进也导致对 提交可能阻塞的任务。一个附加的参数 ForkJoinTask文档提供了一些指导 (基本上:如果它们很小(即使数量很多),我们也会喜欢) 并且没有依赖性)。

...

相关问题