8

TCPストリームからのデータを消費する必要がある複数のスレッドがあります。TCPソケットから読み取るために、共有メモリ内の循環バッファ/キューを使用したいと思います。TCP受信は、循環キューに直接書き込みます。コンシューマーはキューから読み取ります。

この設計では、ゼロコピーとゼロロックを有効にする必要があります。ただし、ここには2つの異なる問題があります。

  1. TCPソケットから1つの論理メッセージだけを読み取ることは可能/効率的ですか?そうでない場合で、複数のメッセージを読んだ場合は、残差をこれからthis->nextにコピーする必要があります。

  2. ロックレスキューを実装することは本当に可能ですか?不可分操作があることは知っていますが、これらもコストがかかる可能性があります。すべてのCPUキャッシュを無効にする必要があるためです。これは、24コアすべてのすべての操作に影響します。

低レベルのTCPで少し錆びており、メッセージがいつ完了したかを正確に判断する方法がわかりません。\ 0を探しますか、それとも実装固有ですか?

ty

4

2 に答える 2

8

残念ながら、TCPはメッセージを転送できず、バイトストリームのみを転送できます。メッセージを転送したい場合は、上にプロトコルを適用する必要があります。高性能に最適なプロトコルは、メッセージの長さを指定する健全性チェック可能なヘッダーを使用するプロトコルです。これにより、データをバイト単位で反復して終了を探すことなく、適切なバッファオブジェクトに正しい量のデータを直接読み取ることができます。 of-メッセージ文字。次に、バッファPOINTERを別のスレッドにキューオフし、次のメッセージのために新しいバッファオブジェクトを作成/デプールできます。これにより、バルクデータのコピーが回避され、大きなメッセージの場合、メッセージオブジェクトポインタに非ブロッキングキューを使用しても意味がないほど効率的です。

利用可能な次の最適化は、オブジェクト* buffersをプールして、継続的な新規/破棄を回避し、ネットワーク受信スレッドで再利用するために*buffersをコンシューマースレッドでリサイクルすることです。これは、ConcurrentQueueを使用して行うのはかなり簡単です。できれば、プールが一時的に空になった場合に、データの破損やsegfaults/AVの代わりにフロー制御を許可するようにブロックします。

次に、各*bufferデータメンバーの先頭に[cachelinesize]'dead-zone'を追加して、スレッドが他のスレッドとデータを誤って共有しないようにします。

その結果、レイテンシー、CPUの浪費、またはキャッシュスラッシングがほとんどない、コンシューマースレッドへの完全なメッセージのハイバンドフローが実現します。24コアすべてが、さまざまなデータでフラットアウトで実行できます。

マルチスレッドアプリでバルクデータをコピーすることは、貧弱な設計と敗北を認めています。

ファローアップ..

プロトコルが異なるため、データの反復に固執しているようです:(

偽共有のないPDUバッファオブジェクト、例:

typedef struct{
  char deadZone[256];  // anti-false-sharing
  int dataLen;
  char data[8388608]; // 8 meg of data
} SbufferData;

class TdataBuffer: public{
private:
  TbufferPool *myPool; // reference to pool used, in case more than one
  EpduState PDUstate; // enum state variable used to decode protocol
protected:
  SbufferData netData;
public:
  virtual reInit(); // zeros dataLen, resets PDUstate etc. - call when depooling a buffer
  virtual int loadPDU(char *fromHere,int len);  // loads protocol unit
  release(); // pushes 'this' back onto 'myPool'
};

loadPDUには、生のネットワークデータへのポインタが渡されます。0を返します-PDUをまだ完全にアセンブルしていないことを意味するか、またはPDUを完全にアセンブルするために生のネットワークデータから取得したバイト数です。この場合、それをキューに入れ、別のPDUをデプールして、loadPDU()を呼び出します。未使用の生データの残りを使用して、次の生データを入力します。

必要に応じて、さまざまな派生バッファクラスのさまざまなプールを使用して、さまざまなプロトコル(TbufferPool [Eprotocols]の配列)を提供できます。TbufferPoolは単なるBlockingCollectionキューである可能性があります。管理はほとんど簡単になります-キューのチェーンの最後で何かがrelease()を呼び出す限り、バッファはシステム全体のキューで送信され、統計を表示するGUIに送信され、次にロガーに送信される可能性があります。

明らかに、「実際の」PDUオブジェクトには、より多くのメソッド、データユニオン/構造体、イテレータ、およびプロトコルを操作するための状態エンジンがロードされますが、それはとにかく基本的な考え方です。主なことは、管理とカプセル化が簡単であり、2つのスレッドが同じバッファーインスタンスで動作することはないため、データの解析/アクセスにロック/シンクロは必要ありません。

そうです。1つのポインタをプッシュ/ポップするために必要な時間より長くロックされたままにする必要がないため、実際の競合の可能性は非常に低くなります。従来のブロッキングキューでさえ、カーネルロックを使用する必要はほとんどありません。

于 2012-07-02T21:18:59.613 に答える
0

Windows8またはWindowsServer2012を使用している場合は、登録済みI / Oを使用できます。これにより、通常のIOCPよりも低いCPUで高い帯域幅が提供されます。これは、カーネル遷移、ゼロコピーなどを削除することによって行われます。

API: http: //msdn.microsoft.com/en-us/library/windows/desktop/ms740642%28v=vs.85%29.aspx

背景情報: http ://www.serverframework.com/asynchronousevents/rio/

于 2014-01-20T05:11:10.097 に答える