2

共有メモリに書き込む「サーバー」プログラムと、メモリから読み取るクライアント プログラムを作成しました。サーバーには、書き込み可能なさまざまな「チャネル」があります。これは、アイテムを追加しているリンクリストが異なるだけです。クライアントは、リンクされたリストのいくつかに関心があり、それらのリストに追加されたすべてのノードを、可能な限り最小限のレイテンシーで読み取りたいと考えています。

クライアントには2つのアプローチがあります。

  1. リンクされたリストごとに、クライアントは「ブックマーク」ポインタを保持して、リンクされたリスト内の場所を維持します。リンクされたリストをラウンド ロビンし、それらすべてを何度も反復し (永遠にループします)、可能であれば各ブックマークを毎回 1 ノードずつ進めます。それができるかどうかは、ノードの「次の」メンバーの値によって決まります。null 以外の場合、次のノードへのジャンプは安全です (サーバーはそれを null から非 null にアトミックに切り替えます)。このアプローチは問題なく機能しますが、繰り返し処理するリストが多数あり、更新を受け取るリストがごくわずかである場合、レイテンシーが悪化します。

  2. サーバーは、各リストに一意の ID を付与します。サーバーがアイテムをリストに追加するたびに、リストの ID 番号もマスターの「更新リスト」に追加します。クライアントは 1 つのブックマーク (更新リストへのブックマーク) のみを保持します。ブックマークの次のポインタが null でないかどうか ( ) を際限なくチェックし、while(node->next_ == NULL) {}そうである場合は先に進み、指定された ID を読み取り、その ID を持つリンク リストの新しいノードを処理します。理論的には、クライアントは毎回すべてのリストを反復処理する必要がないため、多数のリストをより適切に処理できるはずです。

両方のアプローチのレイテンシを (gettimeofday を使用して) ベンチマークしたところ、驚いたことに #2 はひどかった。最初のアプローチは、リンクされたリストの数が少ない場合、多くの場合、20us 未満のレイテンシになります。2 番目のアプローチでは、低レイテンシーの小さなスパッツがありますが、多くの場合、4,000 ~ 7,000us です。

gettimeofday をあちこちに挿入することで、アプローチ 2 で追加されたすべてのレイテンシが、次のポインターが null でないかどうかを繰り返しチェックするループで費やされていることがわかりました。これは私には不可解です。2 番目のアプローチでは、1 つのプロセスでの変更が 2 番目のプロセスに「発行」されるまでに時間がかかっているようです。ある種のキャッシュの相互作用が起こっていると思いますが、私にはわかりません。どうしたの?

更新:もともと、アプローチ#2は条件変数を使用していたため、node->next_ == NULL条件を待機すると、サーバーは更新を発行するたびに条件を通知しました。レイテンシーは同じで、コードを上記のアプローチにまで減らした理由を理解しようとしました。マルチコア マシンで実行しているため、1 つのプロセスのスピンロックが他のプロセスに影響を与えることはありません。

更新 2: node->next_ は揮発性です。

4

3 に答える 3

0

あなたは#2でスピンロックを行っていますが、これは一般的にそれほど素晴らしいアイデアではなく、サイクルをかみ砕いています。

于 2010-03-26T15:35:45.787 に答える
0

yield2 番目のアプローチで、失敗したポーリング試行ごとに追加しようとしましたか? 推測ですが、パワーループを減らすことができます。

Boost.Threadを使用すると、次のようになります。

while(node->next_ == NULL) {
    boost::this_thread::yield( );
}
于 2010-03-26T15:37:48.563 に答える