8

この非常によく似た質問に出くわしましたが、その質問にはQuickFIXのタグが付けられており(これは私の質問には関係ありません)、ほとんどの回答はQuickFIX関連です.

私の質問はもっと広いです。C# を使用してFIX プロトコルメッセージを解析する最も効率的な方法を探しています。<SOH>背景として、FIX メッセージは、ASCII文字 (0x01)で区切られた一連のタグ/値のペアで構成されます。メッセージ内のフィールド数は可変です。

メッセージの例は次のようになります。

8=FIX.4.2<SOH>9=175<SOH>35=D<SOH>49=BUY1<SOH>56=SELL1<SOH>34=2482<SOH>50=frg<SOH>
52=20100702-11:12:42<SOH>11=BS01000354924000<SOH>21=3<SOH>100=J<SOH>55=ILA SJ<SOH>
48=YY77<SOH>22=5<SOH>167=CS<SOH>207=J<SOH>54=1<SOH>60=20100702-11:12:42<SOH>
38=500<SOH>40=1<SOH>15=ZAR<SOH>59=0<SOH>10=230<SOH>

各フィールドでは、タグ (整数) と値 (ここでは文字列) が「=」文字で区切られています。(各タグの正確なセマンティクスはプロトコルで定義されていますが、それはこの質問とは特に関係ありません。)

基本的な構文解析を行う場合、FIX ヘッダーからの特定のタグのほんの一握りだけに関心があり、可能なすべてのフィールドへのランダム アクセスを実際に行うわけではない場合がよくあります。私が検討した戦略は次のとおりです。

  • を使用しString.Split、すべての要素を繰り返し処理し、ハッシュテーブルにタグをインデックス マッピングに配置する - 必要に応じて、すべてのフィールドへの完全なランダム アクセスを提供します。

  • (わずかな最適化) を使用してString.Split、関心のあるタグの配列をスキャンし、タグからインデックスへのマッピングを別のコンテナーに配置します (かなり少数のアイテムである可能性があり、アイテムの数は解析前にわかっているため、必ずしもハッシュテーブルである必要はありません)。

  • String.IndexOf該当するフィールドのオフセットと長さを使用してフィールドごとにメッセージ フィールドをスキャンし、適切な構造体に格納する

最初の 2 つについて - 私の測定値String.Splitはかなり高速であることを示していますが、ドキュメントによると、メソッドは結果の配列の各要素に新しい文字列を割り当てます。これは、多くのメッセージを解析している場合、大量のガベージを生成する可能性があります。.NET でこの問題に取り組むためのより良い方法を見つけられる人はいますか?

編集:

私が省略した 3 つの重要な情報:

  1. タグは、FIX メッセージ内で必ずしも一意であるとは限りません。つまり、特定の状況下では、タグの重複が発生する可能性があります。

  2. 特定のタイプの FIX フィールド<SOH>では、データに「embedded」を含めることができます。これらのタグは「データ」タイプと呼ばれます。辞書には、このタイプのタグ番号がリストされています。

  3. 最終的な要件は、メッセージを編集できることです (特に値を置き換えます)。

4

4 に答える 4

8

これらのメッセージは、ネットワークを介して取得するか、ディスクからロードすることを前提としています。いずれの場合も、これらにバイト配列としてアクセスし、バイト配列を順方向読み取り方式で読み取ることができます。高パフォーマンスが必要/必要/必要な場合は、バイト配列を自分で解析します (高パフォーマンスを得るには、タグと値のハッシュテーブルの辞書を使用しないでください。これは比較すると非常に遅いためです)。バイト配列を自分で解析するということは、関心のないデータの使用を避けることができ、これを反映するように解析を最適化できることも意味します。

ほとんどのオブジェクト割り当てを簡単に回避できるはずです。オブジェクトを作成せずに、FIX float データ型を非常に簡単かつ迅速に double に解析できます (ここでは、独自のバージョンで double.parse を大幅に上回ることができます)。もう少し考える必要があるのは、FIX のシンボル値などの文字列であるタグ値だけです。ここで文字列を作成しないようにするために、各シンボル (値の型) ごとに一意の int 識別子を決定する簡単な方法を考え出すことができます。これは、ヒープへの割り当てを回避するのに役立ちます。

適切に実行されたメッセージのカスタマイズされた最適化された解析は、QuickFix よりも簡単に優れたパフォーマンスを発揮し、.NET または Java でガベージ コレクションなしですべて実行できます。

于 2011-03-04T12:25:44.250 に答える
3

明確で簡単に聞こえるので、私は間違いなくあなたの最初のアプローチを実装し始めます。

Aは私にはとても良いようです、多分などのようなメソッドを公開するクラスDictionary<int,Field>に包まれています...FixMessageGetFieldHavingTag(int tag)

FIXプロトコルはわかりませんが、例を見ると、メッセージは通常短く、フィールドも短いように見えるので、メモリ割り当てのプレッシャーは問題になりません

もちろん、アプローチが自分に適しているかどうかを確認する唯一の方法は、それを実装してテストすることです。

メッセージが多い場合にメソッドが遅いことに気付いた場合は、それをプロファイリングして、問題の原因と場所を特定します。

簡単に解決できない場合は、はい、戦略を変更しますが、最初にテストしてからプロファイルを作成し、最終的に変更する必要があるという考えを強制したいと思います。

したがって、最初の実装後に、多くのメッセージの場合に、多くの文字列の割り当てによってパフォーマンスが低下していることに気付いたと想像してみてください。

そうです、私はあなたの3番目のアプローチと同様のアプローチを取ります。それを「オンデマンド/レイジーアプローチ」と呼びましょう。

FixMessage文字列メッセージを受け取り、メッセージフィールドが必要になるまで何もしないクラスを作成します。
その場合、私はIndexOf要求されたフィールドを検索するために(または同様のものを)使用します。おそらく、別の同等の要求の場合、結果をより速くキャッシュします。

于 2011-02-05T16:50:59.550 に答える
2

私はこれが古い質問への回答であることを知っています.SOにはFIX関連の質問がたくさんあることに最近気づいたので、これに答えることに挑戦したいと思いました.

あなたの質問に対する答えは、実際に解析している特定の FIX メッセージによって異なる場合があります。場合によっては、はい - 文字列に対して「分割」を行うこともできますが、プロトコルで定義されたすべてのメッセージを解析する場合は、参照する以外に選択肢はありません。 FIX データ ディクショナリ、およびメッセージをバイトごとに解析します。これは、仕様によると、FIX メッセージには長さでエンコードされたフィールドがあるためです。このフィールドには、必要なあらゆる種類の「分割」アプローチを妨げるデータが含まれている可能性があります。

これを行う最も簡単な方法は、ディクショナリを参照し、受信したメッセージのタイプ (タグ 35) に基づいてメッセージ定義を取得することです。次に、タグに関連付けられているデータをどのように解析する必要があるかを理解するために、メッセージ定義内の対応するタグ定義を参照して、タグを 1 つずつ抽出する必要があります。これは、メッセージに存在する可能性のある「繰り返しグループ」の場合にも役立ちます。辞書からのメッセージ定義がある場合にのみ、タグが繰り返しグループの開始を表すことを理解できます。

これが役立つことを願っています。参考例が必要な場合は、.NET 用の VersaFix オープンソース FIX エンジンを作成しました。これには、辞書ベースのメッセージ パーサーが含まれています。SVN クライアントを次の場所に指定すると、Subversion サーバーからソース コードを直接ダウンロードできます。

svn://assimilate.com/VfxEngine/Trunk

乾杯。

于 2011-11-15T01:04:24.493 に答える
1

正直なところ、QuickFix を使用し、Managed C++ ラッパーを作成した方がよいでしょう。遅延が気になる場合は、解析の一部として割り当てを実行することはできません。これにより、GC が実行されて FIX エンジンが一時停止する可能性があるためです。一時停止すると、メッセージを送受信できなくなりますが、これは非常に悪いことです。

Microsoft が数年前に、完全に C# で FIX エンジンを構築していると強調した会社が 1 つあります。彼らは、取引日に使用するオブジェクトのプールを構築し、日中は割り当てを実行しませんでした。

レイテンシの要件が何であるかはわかりませんが、私がやっていることについては、コード生成、さまざまな種類のマルチスレッド ヒープを使用してパフォーマンスを向上させ、レイテンシを削減しました。c++ と haskell の混合物を使用します。

要件によっては、パーサーをカーネル モード ドライバーとして実装して、ネットワークから受信したときにメッセージを構築できるようにすることもできます。

@Hans: 10 マイクロ秒は非常に長い時間です。NASDAQ は 98 マイクロ秒で注文を照合し、SGX は、今年新しいプラットフォームを展開する際にクロスするのに 90 マイクロ秒かかると発表しました。

于 2011-02-08T16:45:39.443 に答える