使用参与者进行 Scala 任务并行化 => 调度程序如何工作?



我有一项任务,它可以很容易地分解成多个部分,这些部分可以也应该并行处理以优化性能。

我写了一个制片人-演员,为可以独立处理的任务的每一部分做准备。这种制剂相对便宜。

我写了一个消费者Actor,它处理每个独立的任务。根据参数的不同,每项独立任务可能需要几秒钟的处理时间。所有任务都完全相同。它们都处理相同的算法,具有相同数量的数据(当然是不同的值),从而产生大约相等的处理时间。

因此生产者比消费者快得多。因此,可能很快就准备了200或2000个任务(取决于参数)。所有这些都消耗内存,而只有几个可以同时执行。

现在我看到了两种简单的消耗和处理任务的策略:

  1. 为每个任务创建一个新的使用者参与者实例。

    • 每个使用者只处理一项任务
    • 我假设同时会有许多消费者-参与者实例,而在任何时间点都只能处理其中的几个实例
    • 默认调度程序是如何工作的?每个消费者参与者能否在安排下一个消费者之前完成处理?或者一个使用者会被打断并被另一个使用者取代,从而导致第一个任务完成之前的时间更长?我认为这种actor调度与进程或线程调度不同,但我可以想象,中断仍然有一些缺点(例如,更多的缓存未命中)
  2. 另一种策略是使用消费者参与者的N个实例,并将要处理的任务作为消息发送给它们。

    • 每个使用者依次处理多个任务
    • 由我来为N(消费者数量)找到一个合适的值
    • 任务在N个消费者上的分配也由我决定
  3. 我可以想象一个更复杂的解决方案,在生产者和消费者之间进行更多的协调,但如果不了解调度器,我就无法做出好的决定。

如果手动解决方案不会带来显著更好的性能,我更喜欢默认的解决方案(由Scala世界的某个部分提供),其中调度任务不会留给我(如策略1)。

问题汇总:

  • 默认调度程序是如何工作的?
    • 每个消费者参与者能否在安排下一个消费者之前完成处理
    • 或者一个使用者会被打断并被另一个使用者取代,从而导致第一个任务完成之前的时间更长
    • 当调度器频繁地打断一个参与者并调度另一个参与者时,有什么缺点?缓存未命中
    • 这种中断和调度会像进程调度或线程调度中的上下文变化吗
  • 与这些策略相比,还有什么优势或劣势吗
  • 特别是策略1比策略2有缺点吗
  • 以下哪种策略是最好的
  • 有比我提议的更好的策略吗

我担心,像前两个问题不能得到绝对的回答,但也许这一次是可能的,因为我试图给出一个尽可能具体的案例。

我认为其他问题不用多讨论就可以回答。有了这些答案,就可以选择最适合需求的策略。

我自己做了一些研究和思考,并提出了一些假设。如果这些假设中有任何一个是错误的,请告诉我

如果我是你,我会选择2nd选项。每个任务都有一个新的actor实例,这太乏味了。此外,通过N的智能决策,可以使用完整的系统资源。

尽管这不是一个完整的解决方案。但一个可能的选择是,生产商难道不能停止/放慢生产任务的速度吗?这将是理想的。只有当有消费者可用时,生产者才会生产更多的任务。

假设您使用的是Akka(如果不使用,则应该使用;-)),则可以使用SmallestMailboxRouter来启动多个参与者(也可以添加Resizer),消息分发将根据一些规则进行处理。你可以在这里阅读有关路由器的所有信息。

对于这样一个简单的任务,参与者根本没有利润。将生产者实现为线程,将每个任务实现为Runnable。使用java.util.concurrent中的线程池来运行任务。使用java.util.concurrent.Ssemaphore来限制准备和运行的任务的数量:在创建下一个任务之前,生产者获取信号量,每个任务在执行结束时释放信号量。

最新更新