11

以下是正确的吗?

(当涉及多个生产者(即CAS 操作)时,破坏者模式是否仍然比其他无锁多生产者多消费者队列(例如来自 boost )快得多?)


我的详细情况

处理一个条目可以产生几个新条目,最终也必须处理这些条目。性能具有最高优先级,按 FIFO 顺序处理的条目具有第二优先级。

在当前的实现中,每个线程都使用本地 FIFO,并在其中添加新条目。空闲线程从其他线程的本地 FIFO 窃取工作。线程处理之间的依赖关系使用无锁、机械同理哈希表(写入时的 CAS,具有桶粒度)来解决。这导致争用非常低,但 FIFO 顺序有时会被破坏。

使用破坏者模式将保证 FIFO 顺序。但是,将条目分配到线程上会不会导致比具有工作窃取的本地 FIFO 更高的争用(例如,读取光标上的 CAS)(每个线程的吞吐量大致相同)?


我找到的参考资料

关于破坏者的标准技术文件(第 5 + 6 章)中的性能测试不包括不相交的工作分布。

https://groups.google.com/forum/?fromgroups=#!topic/lmax-disruptor/tt3wQthBYd0是我找到的关于破坏者 + 工作窃取的唯一参考。它指出如果有任何共享状态,每个线程的队列会显着变慢,但没有详细说明或解释原因。我怀疑这句话是否适用于我的情况:

  • 使用无锁哈希表解析共享状态;
  • 必须在消费者之间不连贯地分发条目;
  • 除了工作窃取之外,每个线程只在其本地队列中读写。
4

1 回答 1

14

更新 - 为最大性能提前做好准备:您需要以惯用语法编写破坏者和工作窃取,然后进行基准测试。

对我来说,我认为区别主要在于消息与任务焦点之间的区别,因此也在于您想思考问题的方式。尝试解决您的问题,如果它以任务为中心,那么 Disruptor 非常适合。如果问题是针对消息的,那么您可能更适合其他技术,例如窃取工作。

  • 当您的实施以消息为中心时,使用工作窃取。每个线程都可以获取一条消息并运行它直到完成。例如 HTTP 服务器 - 每个入站 http 请求都分配有一个线程。该线程专注于从头到尾处理请求——记录请求、检查安全控制、进行虚拟主机查找、获取文件、发送响应和关闭连接

  • 当您的实施以任务为中心时,请使用中断器。每个线程都可以在处理的特定阶段工作。替代示例:对于任务焦点,处理将分为多个阶段,因此您将拥有一个执行日志记录的线程、一个用于安全控制的线程、一个用于虚拟主机查找的线程等;每个线程专注于其任务并将请求传递给管道中的下一个线程。阶段可能是并行的,但整体结构是一个专注于特定任务的线程,并在线程之间传递消息。

当然,您可以更改您的实现以更好地适应每种方法。

在您的情况下,如果您想使用 Disruptor,我会以不同的方式构建问题。通常,您可以通过让单个线程拥有状态并通过该工作线程传递所有任务来消除共享状态 - 在 SEDA 中查找很多这样的图表。这可能有很多好处,但同样,这实际上取决于您的实施。

一些更冗长的:

  • Disruptor - 当需要严格的阶段排序时非常有用,当所有任务的长度一致时会有额外的好处,例如:外部系统没有阻塞,每个任务的处理量非常相似。在这种情况下,您可以假设所有线程将在系统中均匀工作,因此安排 N 个线程来处理每 N 条消息。我喜欢将 Disruptor 视为一种在线程处理阶段实现类似 SEDA 的系统的有效方式。您当然可以拥有一个具有单个阶段和多个并行单元在每个阶段执行相同工作的应用程序,但这并不是我的观点。这将完全避免共享状态的成本。
  • 工作窃取 - 当任务具有不同的持续时间并且消息处理的顺序并不重要时使用此选项,因为这允许空闲且已使用其消息的线程继续从另一个任务队列进行。这样,例如,如果您有 10 个线程并且 1 个在 IO 上被阻塞,其余的仍将完成它们的处理。
于 2013-03-12T07:45:50.173 回答