2

我正在为我的应用程序重新设计消息传递系统以使用英特尔线程构建块,并且在尝试在两种可能的方法之间做出决定时感到困惑。

基本上,我有一系列消息对象,并且对于每种消息类型,都有一系列处理程序。对于每个消息对象,我应用为该消息对象类型注册的每个处理程序。


顺序版本将是这样的(伪代码):

for each message in message_sequence                     <- SEQUENTIAL
    for each handler in (handler_table for message.type)
        apply handler to message                         <- SEQUENTIAL

我正在考虑的第一种方法依次(按顺序)处理消息对象并同时应用处理程序。

优点:

  • 消息的可预测顺序(即,我们保证 FIFO 处理顺序)
  • (可能)降低处理每条消息的延迟

缺点:

  • 比单一消息类型的处理程序更多的可用处理资源(糟糕的并行化)
  • 处理器缓存使用不当,因为需要复制消息对象以供每个处理程序使用
  • 小型处理程序的大量开销

这种方法的伪代码如下:

for each message in message_sequence                              <- SEQUENTIAL
    parallel_for each handler in (handler_table for message.type)
        apply handler to message                                  <- PARALLEL

第二种方法是并行处理消息并按顺序将处理程序应用于每条消息。

优点:

  • 更好地使用处理器缓存(将消息对象保持在所有将使用它的处理程序的本地)
  • 小型处理程序不会施加太多开销(只要还有其他处理程序也可以运行)
  • 预期的消息多于处理程序的数量,因此并行性的潜力更大

缺点:

  • 不可预测的顺序 - 如果消息 A 在消息 B 之前发送,它们可能同时被处理,或者 B 可能在 A 的所有处理程序完成之前完成处理(顺序是不确定的)

伪代码如下:

parallel_for each message in message_sequence                     <- PARALLEL
    for each handler in (handler_table for message.type)
        apply handler to message                                  <- SEQUENTIAL

第二种方法比第一种方法有更多的优点,但非确定性排序是一个很大的缺点。

你会选择哪种方法,为什么?是否还有其他方法我应该考虑(除了显而易见的第三种方法:并行消息和并行处理程序,据我所知,它们都有两者的缺点并且没有真正的补救因素)?

谢谢!

编辑:

我想我会默认使用#2,但允许在每条消息上附加一个“对话标签”。任何具有相同标签的消息都会根据其对话顺序进行排序和处理。处理程序会在消息旁边传递对话标签,因此如果需要,他们可以继续对话。像这样的东西:

Conversation c = new_conversation()
send_message(a, c)
...
send_message(b, c)
...
send_message(x)

handler foo (msg, conv)
    send_message(z, c)

...
register_handler(foo, a.type)

a在b之前处理,b在z之前处理。x 可以与 a、b 和 z 并行处理。一旦处理了对话中的所有消息,对话就会被销毁。

4

4 回答 4

1

我会说做一些不同的事情。不要工作发送到线程。当线程完成之前的工作时,让线程拉动工作。

保持固定数量的工作线程(最佳数量等于系统中 CPU 内核的数量),并让每个工作线程在完成前一个任务后从全局队列中按顺序拉取下一个要执行的任务。显然,您需要跟踪消息之间的依赖关系,以推迟对消息的处理,直到其依赖关系得到完全处理。

这可以通过非常小的同步开销来完成 - 可能仅使用原子操作,没有像互斥锁或信号量这样的重型原语。

此外,如果您通过引用将消息传递给每个处理程序,而不是制作副本,让不同 CPU 内核上的不同处理程序同时处理相同的消息实际上可以提高缓存性能,因为更高级别的缓存(通常从 L2 向上)是通常在 CPU 内核之间共享- 因此,当一个处理程序将消息读入缓存时,第二个内核上的另一个处理程序将在 L2 中拥有该消息。所以仔细想想——你真的需要复制这些信息吗?

于 2010-06-13T19:29:52.017 回答
0

第一种方法也具有不可预测的顺序。线程 1 上消息 1 的处理可能需要很长时间,这可能导致消息 2、3 和 4 已被长时间处理

这将使天平转向方法 2

编辑:我明白你的意思。

但是,为什么在方法 2 中您会按顺序执行处理程序。在方法 1 中,排序无关紧要,你可以接受。

例如方法3:同时处理消息和处理程序。

当然,这里的排序也是无保证的。

鉴于处理程序有一些结果,您可能只是将结果存储在有序列表中,这样最终会恢复排序。

于 2010-06-13T10:20:37.243 回答
0

我想它归结为订单是否重要。如果顺序不重要,您可以使用方法 2。如果顺序很重要,您可以使用方法 1。根据您的应用程序应该做什么,您仍然可以使用方法 2,但使用序列号以便所有消息以正确的顺序处理(除非它是您尝试优化的处理部分)。

于 2010-06-13T11:33:04.643 回答
0

如果可能的话,我会通过一些调整来选择第二名。你真的需要每条消息都井井有条吗?我觉得这是一个不寻常的案例。有些消息我们只需要尽快处理,然后有些消息需要在另一条消息之前处理,而不是在每条消息之前处理。

如果有一些消息必须按顺序排列,那么以某种方式标记它们。您可以用一些对话代码标记它们,让处理器知道它必须按照相对于该对话中的其他消息的顺序进行处理。然后,您可以同时处理所有无对话消息和来自每个对话的一条消息。

给您的设计一个漂亮的外观,并确保只有需要按顺序排列的消息。

于 2010-06-13T16:52:25.000 回答