2

インテル スレッディング ビルディング ブロックを使用するようにアプリのメッセージング システムを再設計しており、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

2 番目のアプローチは、メッセージを並行して処理し、ハンドラーを各メッセージに順番に適用することです。

長所:

  • プロセッサー・キャッシュのより良い使用 (メッセージ・オブジェクトを、それを使用するすべてのハンドラーに対してローカルに保ちます)
  • 小さなハンドラーは、それほど多くのオーバーヘッドを課しません (実行する他のハンドラーがある限り)
  • ハンドラーよりも多くのメッセージが予想されるため、並列処理の可能性が高くなります

短所:

  • 予測不可能な順序 - メッセージ A がメッセージ B の前に送信された場合、両方が同時に処理されるか、A のすべてのハンドラーが終了する前に B が処理を終了する可能性があります (順序は非決定論的です)。

擬似コードは次のとおりです。

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

2 番目のアプローチには、最初のアプローチよりも多くの利点がありますが、非決定論的な順序付けは大きな欠点です。

どのアプローチを選択しますか、またその理由は何ですか? 考慮すべき他のアプローチはありますか (明らかな 3 番目のアプローチ: 並列メッセージと並列ハンドラーのほかに、両方の欠点があり、実際の償還要因がないことがわかります)?

ありがとう!

編集:

私がやろうとしていることは、デフォルトで #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 コアの異なるハンドラーで同時に処理すると、実際にはキャッシュのパフォーマンスを向上させることができます。多くの場合、CPU コア間で共有されます。したがって、1 つのハンドラーがメッセージをキャッシュに読み込むと、2 番目のコアの別のハンドラーがこのメッセージを既に L2 に持っています。慎重に考えてください。本当にメッセージをコピーする必要があるのでしょうか。

于 2010-06-13T19:29:52.017 に答える
0

可能であれば、いくつかの調整を加えて2番目に進みます。あなたは本当にすべてのメッセージを整理する必要がありますか?それは珍しいケースだと思います。一部のメッセージはできるだけ早く処理する必要があり、一部のメッセージは別のメッセージの前に処理する必要がありますが、すべてのメッセージの前に処理する必要はありません。

順番に並べる必要のあるメッセージがある場合は、何らかの方法でそれらにマークを付けます。会話内の他のメッセージと比較して順番に処理する必要があることをプロセッサに知らせる会話コードを使用して、それらにマークを付けることができます。次に、すべての会話のないメッセージと各会話からの1つのメッセージを同時に処理できます。

デザインの見栄えを良くし、順番に並べる必要のあるメッセージだけが表示されるようにします。

于 2010-06-13T16:52:25.000 に答える
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 に答える