@jimwiseからの優れたハードウェア/セットアップチューニング回答に加えて、「低レイテンシlinux」は次のことを意味します。
- 決定論的理由(GC開始時の驚きの遅れなし)、低レベル機能へのアクセス(I / O、信号)、言語能力(TMPおよびSTLの完全使用、タイプセーフティ)のためのC ++。
- メモリよりも速度を優先する:512 GBを超えるRAMが一般的です。データベースは、インメモリ、キャッシュされた前払い、またはエキゾチックなNoSQL製品です。
- アルゴリズムの選択:可能な限り高速対正気/理解可能/拡張可能、たとえば、オブジェクトの配列とブールのプロパティの代わりにロックフリーの複数ビット配列。
- 異なるコア上のプロセス間での共有メモリなどのOS機能のフル活用。
- 安全。HFTソフトウェアは通常、証券取引所に共存しているため、マルウェアの可能性は容認できません。
これらの手法の多くはゲーム開発と重複しています。これが、金融ソフトウェア業界が最近の冗長なゲームプログラマーを吸収する理由の1つです(少なくとも、家賃の滞納を支払うまで)。
基本的なニーズは、セキュリティ(株式、商品、為替)価格などの市場データの非常に高い帯域幅ストリームをリッスンし、セキュリティ、価格に基づいて非常に高速な買い/売り/無の決定を行えるようにすることです。と現在の保有。
もちろん、これも見事に間違っている可能性があります。
そこで、ビット配列のポイントについて詳しく説明します。注文の長いリスト(5k IBMの購入、10k DELLの販売など)で動作する高頻度取引システムがあるとします。次のタスクに進むことができるように、すべての注文が満たされたかどうかを迅速に判断する必要があるとしましょう。従来のオブジェクト指向プログラミングでは、これは次のようになります。
class Order {
bool _isFilled;
...
public:
inline bool isFilled() const { return _isFilled; }
};
std::vector<Order> orders;
bool needToFillMore = std::any_of(orders.begin(), orders.end(),
[](const Order & o) { return !o.isFilled(); } );
線形スキャンであるため、このコードのアルゴリズムの複雑さはO(N)になります。メモリアクセスの観点からパフォーマンスプロファイルを見てみましょう。std:: any_of()内のループの各反復は、インライン化されたo.isFilled()を呼び出すため、_isFilled、1バイトのメモリアクセスになります。 (または、アーキテクチャ、コンパイラ、およびコンパイラ設定に応じて4)合計で128バイトのオブジェクトで。したがって、128バイトごとに1バイトにアクセスしています。最悪の場合を想定して1バイトを読み取ると、CPUデータキャッシュミスが発生します。これにより、RAMへの読み取り要求が発生し、RAMから行全体が読み取られます(詳細については、こちらを参照してください)。したがって、メモリアクセスプロファイルはNに比例します。
これと比較してください:
const size_t ELEMS = MAX_ORDERS / sizeof (int);
unsigned int ordersFilled[ELEMS];
bool needToFillMore = std::any_of(ordersFilled, &ordersFilled[ELEMS+1],
[](int packedFilledOrders) { return !(packedOrders == 0xFFFFFFFF); }
これのメモリアクセスプロファイルは、最悪の場合を想定して、ELEMSをRAMラインの幅で除算したものです(さまざま-デュアルチャネルまたはトリプルチャネルなど)。
そのため、実際には、メモリアクセスパターンのアルゴリズムを最適化しています。RAMの量は役に立ちません-この必要性を引き起こすのはCPUデータキャッシュサイズです。
これは役立ちますか?
YouTubeには、低レイテンシプログラミング(HFT用)に関するすべての優れたCPPConトークがあります:https : //www.youtube.com/watch?v =NH1Tta7purM