低遅延Unix / Linux


11

ほとんどの低レイテンシ/高頻度のプログラミングジョブ(ジョブの仕様に基づく)は、UNIXプラットフォームに実装されているようです。多くの仕様で、彼らは「低遅延linux」タイプの経験を持つ人々に特別な要求をしています。

これがリアルタイムのLinux OSを意味しないと仮定すると、人々はこれが何を指しているのか私に助けを与えることができますか?CPUアフィニティをスレッドに設定できることは知っていますが、それ以上のことを要求していると思います。

カーネルのチューニング?(とにかく、ソーラーフレアのようなメーカーはカーネルバイパスネットワークカードを製造していると聞きましたが)?

DMAまたはプロセス間の共有メモリはどうですか?人々が私に簡単なアイデアを与えてくれれば、Googleで調査をすることができます。

(この質問には、おそらく高頻度取引に精通した人が必要です)


2
カーネルチューニングは、非リアルタイムOSをできるだけリアルタイムにする方法です。スレッドの固定も必須です。あなたはこの記事では、よりそのことについて読むことができます:coralblocks.com/index.php/2014/04/...
rdalmeida

回答:


26

私は、IBおよびヘッジファンドの設定でHFTグループをサポートするためにかなりの量の仕事をしました。sysadminビューから回答しますが、その一部はそのような環境でのプログラミングにも適用できます。

雇用主が「低遅延」サポートに言及するときに通常求めているものがいくつかあります。これらのいくつかは「生の速度」の質問です(購入する10gカードの種類と、それを入れるスロットを知っていますか?)が、それらの多くは、高頻度取引環境が従来のものと異なる方法に関するUnix環境。いくつかの例:

  • Unixは伝統的に、リソースを枯渇させずに多数のプロセスの実行をサポートするように調整されていますが、HFT環境では、コンテキスト切り替えなどのオーバーヘッドを最小限に抑えて1つのアプリケーションを実行したい場合があります。古典的な小さな例として、Intel CPUでハイパースレッディングを有効にすると、より多くのプロセスを一度に実行できますが、個々のプロセスの実行速度に大きなパフォーマンスの影響があります。プログラマーとして、スレッド化やRPCなどの抽象化のコストを検討し、モノリシックなソリューション(クリーンではないが)がオーバーヘッドを回避できる場所を把握する必要があります。

  • TCP / IPは通常、接続のドロップを防ぎ、利用可能な帯域幅を効率的に使用するように調整されます。目標が非常に高速なリンクから可能な限り最小の遅延を取得することである場合(より制限されたリンクから可能な限り高い帯域幅を取得するのではなく)、ネットワークスタックのチューニングを調整する必要があります。プログラミングの側面からも、同様に利用可能なソケットオプションを確認し、遅延を減らすためよりも帯域幅と信頼性のために調整されたデフォルトがあるものを見つけたいと思うでしょう。

  • ネットワークの場合と同様に、ストレージの場合も同様に、アプリケーションの問題からストレージのパフォーマンスの問題を特定する方法を知り、プログラムのパフォーマンスを妨げる可能性が最も低いI / O使用のパターンを学習します。たとえば、非同期IOを使用することの複雑さがどこに見合うか、そして欠点は何であるかを学びます。

  • 最後に、さらに痛いことに:私たちのUnix管理者は、監視する環境の状態に関する情報をできるだけ多く必要としているため、SNMPエージェントのようなツール、Nagiosのようなアクティブな監視ツール、sar(1)のようなデータ収集ツールを実行したいと考えてます。ただし、コンテキストスイッチを絶対に最小限に抑え、ディスクとネットワークIOの使用を厳密に制御する必要がある環境では、監視の費用と監視対象のボックスのベアメタルパフォーマンスとの適切なトレードオフを見つける必要があります。同様に、コーディングを容易にしますが、パフォーマンスが低下するテクニックを使用していますか?

最後に、時間とともに来るものが他にもあります。経験から学ぶコツと詳細。しかし、これらはより専門的であり(epollを使用するのはなぜですか?理論的に同一のPCIeコントローラーを備えたHPサーバーの2つのモデルはそれほど異なるパフォーマンスを発揮するのはなぜですか?)、特定のショップが使用しているものに関連しており、年ごとに変更される可能性が高くなります。


1
プログラミングの答えに興味がありましたが、これは非常に有用で有益でした。
user997112

5
@ user997112これはプログラミングの答えです。そうでない場合は、読むまで読み続けてください:)
ティムポスト

15

@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


「オブジェクトの配列とブール属性の代わりの複数ビット配列」とはどういう意味ですか?
user997112

1
例とリンクを詳しく説明しました。
JBRウィルキンソン

注文が満たされているかどうかを示すためにバイト全体を使用するのではなく、さらに一歩進んで、1ビットだけを使用できます。したがって、単一のキャッシュライン(64バイト)で、256オーダーの状態を表すことができます。だから-ミスが少ない。
quixver

また、メモリのリニアスキャンを実行している場合、ハードウェアプリフェッチャーはデータをロードするという素晴らしい仕事をします。メモリにシーケンシャルにアクセスするか、ストライドまたは単純な何かにアクセスすることを提供します。しかし、何らかの非順次的な方法でメモリにアクセスしている場合、CPUプリフェッチャーは混乱します。たとえば、バイナリ検索。その時点で、プログラマはヒントでCPUを支援できます-_mm_prefetch。
quixver

-2

1つまたは2つの高周波ソフトウェアを実稼働に使用していなかったため、最も重要なことを言います。

  1. ハードウェア構成およびシステム管理者とネットワークエンジニアは、取引システムで処理された注文数の良い結果を定義していませんが、上記の基本を知らない場合は大幅にダウングレードできます。
  2. 実際にシステムで高頻度取引を行う唯一の人物は、C ++でコードをまとめるコンピューター科学者です。

    使用される知識の中には

    A.操作の比較と交換。

    • CASがプロセッサでどのように使用され、コンピューターがどのようにそれをサポートしていわゆるロックなし構造処理で使用されるか。またはロックフリー処理。ここでは本全体を書きません。簡単に言えば、GNUコンパイラとMicrosoftコンパイラはCAS命令の直接使用をサポートしています。キューから要素を抽出したり、キューに新しい要素を入れたりするときに、コードに「No.Wair」を設定できます。
  3. 才能のある科学者はもっと使います。彼は最近の新しい「パターン」で、Javaに最初に登場したものを見つけるはずです。DISRUPTORパターンと呼ばれます。ヨーロッパのLMAX取引所では、dayaキューが最新のCPUキャッシュのサイズと一致していない場合、CPUによるメモリキャッシュの解放でスレッドベースの使用は最新のプロセッサでの処理時間を減らすと高周波コミュニティに説明しました= 64

    そのため、彼らは、マルチスレッドプロセスがハードウェアCPUキャッシュを競合解決なしで正しく使用できるようにするJavaコードを公開しました。そして、優れたコンピューター科学者は、そのパターンがすでにC ++に移植されているか、自分で移植を行うことを知っています。

    これは、管理者の設定を超えた熟練した方法です。これは今日の高頻度の真の中心にあります。

  4. コンピューターサイエンスの男は、QAの人々を助けるためだけでなく、多くのC ++コードを書く必要があります。しかし、また
    • トレーダーで検証し、実績のある達成された速度に直面する
    • さまざまな古いテクノロジーの使用を非難し、独自のコードでそれらを公開して、良い結果が得られないことを示す
    • 古いテクノロジーを再び使用する代わりに、実証済みのpupe / select kernel速度に基づいて、hos独自のマルチスレッド通信c ++コードを記述します。例を挙げましょう-最新のtcpライブラリはICEです。そして、それをした人々は明るいです。しかし、彼らの優先事項は、多くの言語との互換性の分野にありました。そう。C ++の方がうまくいくことができます。そのため、非同期選択呼び出しに基づいて最高のパフォーマンスのサンプルを検索します。そして、複数の消費者、複数の生産者、HFには向かない。
      そして、到着したメッセージのカーネル通知にのみパイプが使用されることに驚くでしょう。64ビットのメッセージ番号をそこに置くことができます-しかし、コンテンツのためにあなたはノーロックCASキューに行きます。非同期カーネルselect()呼び出しによってトリガーされます。
    • さらに。メッセージのパイピング/キューイングを行うスレッドへのc ++スレッドアフィニティを使用した割り当てについて説明します。そのスレッドにはコアアフィニティが必要です。誰も同じCPUコア番号を使用しないでください。
    • 等々。

ご覧のとおり、高頻度は開発フィールドです。C ++プログラマーだけで成功することはできません。

そして、私が成功すると言うとき、あなたが働くヘッジファンドは、人々とリクルーターが話している数を超えて、年間の報酬でツアーの努力を認めることを意味します。

単純なコンストラクタ/デストラクタFAQの時代は永遠に過ぎ去りました。そして、c ++自体はメモリ管理から解放し、クラスの大きな深さの非継承を強制するために新しいコンパイラに移行しました。時間の無駄。パラダイムを再利用するコードが変更されました。ポリモーフで作成したクラスの数だけではありません。これは、再利用可能なコードのパフォーマンスをまっすぐに確認したものです。

そのため、そこで学習曲線に入るかどうかを選択します。一時停止の標識にぶつかることはありません。


6
スペルと書式設定に多少の労力をかけることができます。現在の形式では、この投稿はほとんど理解できません。
CodesInChaos

1
10年前の状況を説明します。ハードウェアベースのソリューションは、C ++がどの程度最適化されていても、今日では純粋なC ++よりも簡単に優れています。
シェード

ハードウェアベースのソリューションとは何かを知りたい人のために-コードは実際には高速メモリに焼き付けられ、いわゆるROMメモリの再焼き付けによって変更されないのはほとんどがFPGAソリューションです。読み取り専用
アレックスp

@alexpあなたは明らかにあなたが話していることを知らない。FPGAは、「高速メモリに焼き付けられたコード」とは異なるものです。
シェード
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.