12

カスタム ネットワーク プロトコル ライブラリを使用しています。このライブラリは TCP/IP 上に構築されており、高頻度のメッセージングで使用されると想定されています。これはノンブロッキング ライブラリであり、呼び出し元と統合するためのインターフェイスとしてコールバックを使用します。

私はパフォーマンスの専門家ではないので、ここでこの質問をすることにしました。カスタム ライブラリには、以下に概説する特定の制約があります。

「呼び出し先は、コールバック スレッドのコンテキストでライブラリの API を呼び出さないでください。そうしようとすると、スレッドがハングします」

API の制限を克服する唯一の方法は、メッセージを処理し、ライブラリを呼び出して応答を送信する別のスレッドを開始することです。ライブラリ スレッドとプロセス スレッドは共通のキューを共有し、ミューテックスによって保護され、wait_notify()呼び出しを使用してメッセージの存在を示します。

1 秒あたり 80,000 件のメッセージを受信して​​いる場合、スレッドをスリープ状態にしてかなり頻繁に起動し、1 秒あたり最大 80,000 回スレッド コンテキスト スイッチを実行します。

さらに、2 つのスレッドがあるため、L1 キャッシュ内のメッセージ バッファーを共有しません。メッセージを含むキャッシュ ラインは、最初にライブラリのスレッドによって埋められ、次に削除され、プロセス スレッドのコアの L1 キャッシュに取り込まれます。何か不足していますか、それともライブラリの設計が高パフォーマンスのユースケース向けではない可能性がありますか?

私の質問は次のとおりです。

  1. 「ロックが発生する可能性があるため、コールバックのコンテキストでこの API を使用しないでください」などの警告を見たことがあります。多くのライブラリにまたがっています。このような設計上の制約を引き起こす一般的な設計上の選択は何ですか? 同じスレッドがロックを複数回呼び出すという単純な問題であれば、再帰ロックを使用できます。これは再入可能の問題ですか? API 所有者が非再入可能 API を作成する原因となる問題は何ですか?

  2. 上記の設計モデルで、ライブラリ スレッドとプロセス スレッドが同じコアを共有し、結果としてキャッシュ ラインを共有できる方法はありますか?

  3. sig_atomic_t2 つのスレッド間でデータを共有するメカニズムとしてvolatile はどれくらい高価ですか?

  4. 高頻度のシナリオで、2 つのスレッド間で情報を共有するための軽量な方法は何ですか?

ライブラリと私のアプリケーションは、C++ と Linux で構築されています。

4

2 に答える 2

6

2 つのスレッドが同じキャッシュ ラインを共有するにはどうすればよいですか?

スレッドはキャッシュ ラインとは関係ありません。少なくとも明示的ではありません。うまくいかないのは、コンテキスト スイッチでのキャッシュ フラッシュと TLB の無効化ですが、スレッドの仮想アドレス マッピングが同じであれば、キャッシュは通常、これらのことを認識しないはずです。

このような設計上の制約を引き起こす一般的な設計上の選択は何ですか?

ライブラリの実装者は、次のことを扱いたくありません。

  1. 複雑なロック方式。
  2. 再入可能なロジック (つまり、'send()' を呼び出すと、ライブラリは であなたを呼び戻しon_error()、そこからsend()再度呼び出します。これには特別な注意が必要です)。

個人的には、高パフォーマンス、特にネットワーク関連のことになると、コールバックを中心に設計された API を持つことは非常に悪いことだと考えています。ただし、ユーザーと開発者の両方にとって (コードの書きやすさという点で) 作業が大幅に簡素化されることもあります。これに対する唯一の例外は CPU 割り込み処理かもしれませんが、それは別の話であり、API とはほとんど言えません。

同じスレッドがロックを複数回呼び出すという単純な問題であれば、再帰ロックを使用できます。

再帰的ミューテックスは比較的高価です。実行時の効率を気にする人は、可能な限りそれを避ける傾向があります。

上記の設計モデルで、ライブラリ スレッドとプロセス スレッドが同じコアを共有し、結果としてキャッシュ ラインを共有できる方法はありますか?

はい。両方のスレッドを同じ CPU コアに固定する必要があります。たとえば、 を使用しsched_setaffinity()ます。しかし、これは単一のプログラムにとどまらず、環境全体を正しく構成する必要があります。たとえば、OS がそのコアで 2 つのスレッド (割り込みを含む) 以外を実行できないようにし、これら 2 つのスレッドが別の CPU に移行できないようにすることを検討したい場合があります。

2 つのスレッド間でデータを共有するメカニズムとして、揮発性の sig_atomic_t はどれくらい高価ですか?

それ自体は高価ではありません。ただし、マルチコア環境では、キャッシュの無効化、ストール、MESI トラフィックの増加などが発生する可能性があります。両方のスレッドが同じコア上にあり、侵入するものがない場合、唯一のペナルティは、変数をキャッシュできないことです。キャッシュされるべきではないため、これは問題ありません (つまり、コンパイラは、キャッシュであろうとメインメモリであろうと、常にメモリからフェッチします)。

高頻度のシナリオで、2 つのスレッド間で情報を共有するための軽量な方法は何ですか?

同じメモリに対して読み書きします。システム コールやブロッキング コールなどを使用しない可能性があります。たとえば、少なくとも Intel アーキテクチャでは、メモリ バリアのみを使用して 2 つの同時スレッドに対してリング バッファを実装できます。そのためには、細部まで徹底的にこだわる必要があります。ただし、何かを明示的に同期する必要がある場合は、アトミック命令が次のレベルになります。Haswell には、低オーバーヘッドの同期に使用できるトランザクション メモリも付属しています。その後、何も速くありません。

また、Intel Architectures Developer's Manual の第 11 章のメモリ キャッシュと制御についても参照してください。

于 2013-01-17T04:39:25.467 に答える
1

ここで留意すべき重要なことは、ネットワーク アプリケーションで作業する場合、より重要なパフォーマンス メトリックは「タスクあたりのレイテンシ」であり、アプリケーション全体の生の CPU サイクル スループットではないということです。そのため、スレッド メッセージ キューは、可能な限り迅速にアクティビティに応答するための非常に優れた方法である傾向があります。

今日のサーバー インフラストラクチャ (または私の Core i3 ラップトップでさえも) で 1 秒あたり 80,000 件のメッセージを処理することは、取るに足らない領域に近づいています (特に L1 キャッシュのパフォーマンスに関する限り)。スレッドがかなりの量の作業を行っている場合、メッセージが処理されるたびに CPU が L1 キャッシュを介してフラッシュすることを期待するのはまったく不合理ではありません。メッセージがまったく多くの作業を行っていない場合は、 L1 ポリシーに関係なく、おそらく CPU 負荷の 1% 未満になるため、問題ではありません。

メッセージングのその速度では、パッシブ スレッド モデルをお勧めします。メッセージを処理するためにスレッドが起動され、その後スリープ状態に戻るスレッド。これにより、最適なレイテンシ対パフォーマンス モデルが得られます。たとえば、これは最もパフォーマンス効率の高い方法ではありませんが、ネットワーク リクエストにすばやく応答するのに最適です (通常、ネットワーク プログラミングを行う場合は、この方法を優先します)。

今日のアーキテクチャ (2.8GHz、4+ コア) では、1 秒間に 100 万件のキュー メッセージを処理することを期待しない限り、生のパフォーマンスについて心配することさえありません。それでも、メッセージが実行すると予想される実際の作業量に少し依存します. いくつかのパケットを準備して送信する以上のことが期待されていない場合、1 ミルは間違いなく控えめです。

上記の設計モデルで、ライブラリ スレッドとプロセス スレッドが同じコアを共有し、結果としてキャッシュ ラインを共有できる方法はありますか?

いいえ、つまり、独自のオペレーティング システムを展開したい場合は確かに存在します。しかし、CPU を他のタスクと共有することを期待してマルチタスク環境で実行したい場合は、「いいえ」です。また、スレッドをコアにロックすることは、スレッドの平均応答時間を損なう可能性が非常に高く、パフォーマンスの向上にはあまり貢献しません。(そして、パフォーマンスの向上は、システムがソフトウェア専用に使用されていることの影響を受け、おそらく複数のタスクを実行しているシステムでは蒸発します)

高頻度のシナリオで、2 つのスレッド間で情報を共有するための軽量な方法は何ですか?

メッセージ キュー。:) 真剣に。ばかげているように聞こえるつもりはありませんが、それがメッセージ キューです。それらは 2 つのスレッド間で情報を共有し、通常は軽量です。コンテキストの切り替えを減らしたい場合は、一定数のメッセージが蓄積された後 (またはアクティビティが少ない場合は一定のタイムアウト期間) にのみ、キューを空にするようにワーカーに通知します。

于 2013-01-21T07:20:16.160 に答える