アーキテクチャ

普通のノート PC でも速い理由。

取引画面は重いものです。長い履歴、止まらないティック、1 ページに並ぶ多くのチャート。TradeCanvas は各フレームに必要な仕事だけを、メインスレッドで、サードパーティのコードなしで行います。このページではその方法と、まだ改善中の点を示します。

開発中: エンジンは実用に十分な速さですが、到達すべき速さにはまだ届いていません。最後のセクションに残りの課題を挙げています。

01

小さなパッケージ、一方向の依存

各パッケージは下にあるパッケージにだけ依存します。TradeCanvas 以外のコードはページで動きません。フレームワーク用コンポーネントは React・Vue・Svelte を peer として受け取り、WebGL レンダラーはチャートが求めたときだけ読み込む別チャンクです。

0 実行時のサードパーティ依存

02

ティックがたどる道

フィードからの価格は 1 フレームで画面に届きます。各段階は変わった部分だけを処理します。

  1. 1あなたのフィードDataAdapterWebSocket、REST、証券会社: 履歴とストリームがあれば何でも。
  2. 2ストリーム管理StreamManager先に履歴、次にライブ。古いリクエストの応答は捨て、黙ったフィードは 30 秒以内に気づきます。
  3. 3ティック集約TickAggregatorティックは 1,000 件のリングバッファーに入り、その時間足の足にまとめられます。
  4. 4指標IndicatorEngine系列の最初からではなく、最後の足から再計算します。
  5. 5ビューポートViewport画面に見える足だけを走査し、価格軸もそれだけから決めます。
  6. 6描画ループRenderLoopティックがいくつ来ても、シーンの描画はアニメーションフレームごとに最大 1 回です。
  7. 7Canvas 2D または WebGLRenderEngineキャンバス 2 枚、WebGL をオンにするとその下に GPU キャンバス。
03

ティック 1 つは足 1 本分のコスト、履歴全体ではありません

ティックが最後の足を変えると、各指標は系列全体を再計算せず、その足(と式が振り返る数本)から続きを計算します。覚えている足が一致しなくなれば全体の再計算に戻るので、データの訂正を見逃しません。

約 20 の指標が独自の増分更新を持ち、さらに 31 の指標が点ごとに続きを計算する基盤の上に作られています。他の指標の線を読む指標も続きから計算します。

0 / 64 指標 4 つ、10 万本で測定: 1 ティック約 0.001 ms
04

縮小したら、ピクセルごとに 1 列

足が 1 ピクセルより細くなると、1 本ずつ描くのは目に見えない無駄です。ローソク足、バー、高安、出来高はピクセルごとに 1 列で描き、その下の足の最初の始値、最高値、最安値、最後の終値を使います。線とエリアはピクセルあたり約 2 点 (LTTB)、指標の線と帯はピクセル列ごとに 1 区間を描きます。

表示中の足
313
ピクセルあたりの足
0.39
描いた図形
313

足は CSS ピクセルの 4 分の 1 より細くなりません。そのため 100 万本を持つチャートでも最大まで縮小すると表示はピクセルあたり約 4 本、1,600 px のチャートで約 6,400 本です。100 万本ベンチマークは残りをパンする性能を測ります。

05

キャンバス 2 枚、だからホバーはほぼ無料

シーン(グリッド、系列、指標、描画、注文)は 1 枚のキャンバスに、クロスヘア、凡例、軸ラベル、カウントダウンはその上の薄いキャンバスにあります。マウスを動かすと上のキャンバスだけを描き直します。データの更新は次のフレームでシーンを 1 回描きます。

全画面サイズのキャンバスごとに、ブラウザーはフレームごとに合成を 1 回増やします。そのためエンジンはキャンバスを少なく保ち、2D 専用の描画用の裏キャンバスは必要なときだけ置きます。

06

書き直さず、記録して WebGL へ

チャートは Canvas 2D の呼び出しで描きます。WebGL がオンなら、その呼び出しはレコーダーに渡り、パス、塗り、線がデバイスピクセル単位の GPU コマンドになります。GPU が同じように描けないものは順序を保ったまま Canvas 2D に残るので、どちらでも絵は同じです。

  • renderer: 'auto' は大きな性能上の注意がないコンテキストを求めるので、WebGL がソフトウェアで動く環境では Canvas 2D のままです。
  • 1 ページで WebGL コンテキストを持てるチャートは最大 8 つです (setMaxWebGLCharts)。残りは待ち行列に入り Canvas 2D で描きます。
  • GPU がコンテキストを失っても、チャートは Canvas 2D で描き続け、コンテキストが戻れば WebGL で組み直します。
  • ローソク足はシェーダーと CPU で同じ規則によりデバイスピクセルに揃えられ、両方のレンダラーが同じピクセルに描きます。
07

非力な環境のために

  • 既定は Canvas 2DWebGL はより多くの環境で測定されるまでオプトインです。高いピクセル比の内蔵グラフィックスでは、描画ではなくブラウザーの合成がボトルネックです。
  • リサイズはフレームに 1 回リサイズイベントはフレームごとに 1 回にまとめられ、サイズが変わらなければ何もしません。
  • 履歴はページ単位過去へスクロールすると古い足を 500 本ずつ、一度に 1 リクエスト、失敗後は間隔を空けながら読み込みます。
  • 読み込みのちらつきなし読み込み表示は 200 ms 待つので、素早い銘柄切り替えでちらつきません。
  • 小さなダウンロードヘッドレスチャートは gzip 後 176 KB、ウィジェット全体で 315 KB、WebGL レンダラーは使うときに 18 KB 追加です。
  • その場で更新される株価ボードbo-grid の株価ボードはティックをフレームごとに 1 回のパッチにまとめ、変わったセルだけを描き直します。
08

まだ改善中のこと

エンジンは完成していません。ソースから分かっているコストで、次に取り組む項目です。

  1. 指標はフレームごとに 1 回ではなく、足が更新されるたびに更新されます。フレームあたりのティックが多いフィードでは、描画より多く計算します。
  2. 足はオブジェクトの配列で保持しています。型付き配列による列指向の保存なら、非常に長い履歴でメモリが減り走査も速くなります。
  3. すべてメインスレッドで動きます。指標計算用のワーカーはコードにありますが、まだチャートにつながっていません。
  4. 板のヒートマップはスナップショットを通常の配列に持ち、フレームごとに全価格帯を走査して最大値を探します。出来高プロファイルは描画のたびにバケットを確保します。
  5. 一部のオブジェクト(パス、小さな配列)はまだフレームごとに確保され、ガベージコレクターの仕事になります。
  6. 内蔵グラフィックスで 2560×1400、ピクセル比 2 では合成だけで約 23 ms かかり、そこでの WebGL フレームは平均 29 ms です。
あなたの環境で測る →監査ページはこれらのシーンをあなたのブラウザーで実行します: Canvas 2D と WebGL の 100 万本、10 万本上のライブティック、1,000 銘柄の株価ボード。