当前位置: 首页 > 知识库问答 >
问题:

Akka如何从ForkJoinPool中受益?

水渊
2023-03-14

Akka docs声明默认调度程序是一个fork-join-在大多数情况下提供出色的性能
我想知道为什么?

从 ForkJoinPool

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

起初,我想Akka不是案例(1)的一个例子,因为我不知道Akka如何分叉任务,我的意思是,在许多任务中可以分叉的任务是什么?
我将每条消息视为一个独立的任务,这就是为什么我认为Akka类似于案例(2),其中消息是许多小任务正在提交(通过!和?)到ForkJoinPool

下一个问题,虽然与akka没有严格关系,但将是,为什么一个用例,其中分叉和连接(ForkJoinPool的主要功能,允许工作窃取)没有被使用,仍然可以从ForkJoinPool中受益?
从分叉加入池的可伸缩性

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

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

所以,我的问题是:

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

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

>

  • Akka不使用分叉和连接功能,因为AFAIK与Actor模型,或者至少我们如何实现它,并没有真正的用例(来自johanandren的评论)。
    因此,我对Akka不是案例(1)的实例的理解是正确的。
  • 在我最初的答案中,我说外部客户端提交的所有任务都将在共享队列中排队。
    这是正确的,但仅适用于FJP的先前版本(jdk7)。在jdk8中,单个提交队列被许多“提交队列”所取代。这个答案很好地解释了这一点:

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

    因此,这允许在不使用分叉连接的情况下窃取工作。正如道格·李所说

    当大量客户端提交大量任务时,吞吐量明显提高。(我已经在微基准测试中测量了高达60倍的加速)。这个想法是以类似于工人的方式对待外部提交者 - 使用随机排队和偷窃。(这需要进行大规模的内部重构,以解除工作队列和工作线程的关联。当所有任务都是异步的并提交到池而不是分叉时,这也大大提高了吞吐量,这成为构建执行组件框架以及许多可能使用ThreadPoolExecutor的普通服务的合理方法。

    >

  • 关于FJP,还有另一个值得一提的奇点,取自此评论

    4%对FJP来说确实不多。你仍然需要注意FJP的权衡:FJP保持线程旋转一段时间,以便能够更快地处理及时到达的工作。这在许多情况下可确保良好的延迟。但是,尤其是在池过度配置的情况下,在几乎空闲的情况下,权衡是一点延迟与更多功耗。

  • 共有2个答案

    常光明
    2023-03-14

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

    分叉连接池的可扩展性

    由于Doug Lea开发的新fork join executor,Akka 2.0消息传递吞吐量在多核硬件上比以前的版本扩展得更好。一个微基准表明吞吐量增加了1100%!

    ...

    http://cs . OS wego . edu/piper mail/concurrency-interest/2012-January/008987 . html

    ...

    亮点:

      < li >当大量客户端提交大量任务时,吞吐量显著提高。(我在微基准测试中测量了高达60倍的加速)。这个想法是以类似于工人的方式对待外部提交者——使用随机排队和窃取。(这需要一个大的内部重构来分离工作队列和工人。)当所有任务都是异步提交到池而不是分叉时,这也大大提高了吞吐量,这成为构造actor框架以及许多普通服务的合理方式,否则您可能会使用ThreadPoolExecutor。

    这些改进还可以减少提交可能阻塞的任务的敌意。ForkJoinTask文档中增加了一个范例,提供了一些指导(基本上:如果它们很小(即使很多)并且没有依赖关系,我们也会喜欢它们)。

    ...

    曾嘉祯
    2023-03-14

    Akka 中的 FJP 在 asyncMode = true 的情况下运行,因此对于第一个问题 - 让外部客户端提交短/小型异步工作负载。每个提交的工作负载要么调度一个参与者来处理来自其收件箱的一条或几条消息,但它也用于执行Scala Future操作。

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

    请注意,默认情况下,我们目前在Java8 FJP的分叉版本上运行,因为我们看到Java9 FJP的吞吐量大幅下降(它包含相当多的变化)。如果您感兴趣,这是讨论的问题#21910。此外,如果您想使用不同池的基准测试,您可以在此处找到一些*池基准测试:https://github.com/akka/akka/tree/master/akka-bench-jmh/src/main/scala/akka/actor

     类似资料:
    • 问题内容: 跟进如何在线程池中使用MDC?如何将MDC与?具体来说,我如何在执行任务之前包装一个MDC值? 问题答案: 以下内容似乎对我有用: 和 针对您的任务而不是普通的ForkJoinPool 运行任务。 代替扩展。

    • 当接收到消息时,它将运行,并将接收到的每个项发布到。 我怎么能那么做? 以防万一它可能会添加更多选项,请注意,另一个代码块是的Websocket处理程序。

    • 我读过以下文档https://doc.akka.io/docs/akka/current/general/message-delivery-reliability.html#deal-letters,其中写道: 参与者可以订阅事件流上的类Akka.actor.deadletter,请参见event stream以了解如何做到这一点。 但是在Akka Typed中,中没有名为的方法。

    • 我正在尝试使用akka流传输一个文件,在将流的结果提取到Future[String]时遇到了一个小问题: 我得到一个编译错误: 任何人都可以帮助我了解我做错了什么,以及我需要做些什么来提取流的结果?

    • 是否可以配置使用1个执行线程? 我正在执行在中调用的代码。每次它运行时,我都会遇到不同的运行时行为,这使得研究回归变得很困难。 我希望代码库提供“调试”和“发布”模式。“调试”模式将使用固定种子配置,并使用单个执行线程配置。“释放”模式将使用系统提供的种子,并使用默认数量的线程。 我尝试配置的并行性为1,但它使用2个线程(和第二个工作线程)。有什么想法吗?

    • 我正在尝试使用circe在Akka-Http应用程序中执行我的JSON(de)序列化,而不是Spray-JSON。因此,我想使用指令和来获取请求主体的字符串表示,然后执行自己的反序列化。但是似乎太聪明了,它拒绝任何内容类型不为的东西。 我现在明白了,我误解了,它只会缩小而不会扩大解封程序将接受的内容类型范围。这就解释了为什么我的解决方案不起作用。