Arsitektur

Bagaimana ia tetap cepat di laptop biasa.

Layar trading itu berat: riwayat panjang, tick yang tak pernah berhenti, banyak grafik dalam satu halaman. TradeCanvas bekerja sesedikit yang dibutuhkan setiap frame, di main thread, tanpa kode pihak ketiga. Halaman ini menunjukkan caranya, dan di mana ia masih diperbaiki.

Masih dalam pengerjaan: mesinnya cukup cepat untuk dirilis, tetapi belum secepat nantinya. Bagian terakhir mencantumkan apa yang kami tahu masih tersisa.

01

Paket kecil, satu arah

Setiap paket hanya bergantung pada paket di bawahnya. Tidak ada kode dari luar TradeCanvas yang berjalan di halaman Anda: komponen framework menjadikan React, Vue, atau Svelte sebagai peer, dan perender WebGL adalah chunk terpisah yang hanya dimuat saat sebuah grafik memintanya.

0 dependensi runtime pihak ketiga

02

Perjalanan sebuah tick

Harga datang dari feed Anda dan sampai ke layar dalam satu frame. Setiap tahap hanya mengerjakan bagian yang berubah.

  1. 1Feed AndaDataAdapterWebSocket, REST, broker: apa pun yang punya riwayat dan stream.
  2. 2Pengelola streamStreamManagerRiwayat dulu, lalu langsung. Jawaban dari permintaan lama dibuang; feed yang diam terdeteksi dalam 30 detik.
  3. 3Agregator tickTickAggregatorTick masuk ke ring buffer berisi 1.000 dan dilipat ke bar kerangka waktunya.
  4. 4IndikatorIndicatorEngineDihitung ulang dari bar terakhir, bukan dari awal seri.
  5. 5ViewportViewportHanya bar di layar yang ditelusuri; skala harga berasal dari bar itu saja.
  6. 6Loop renderRenderLoopBerapa pun tick yang datang, adegan digambar paling banyak sekali per frame animasi.
  7. 7Canvas 2D atau WebGLRenderEngineDua kanvas, dan kanvas GPU di bawahnya saat WebGL aktif.
03

Satu tick seharga satu bar, bukan seluruh riwayat

Saat tick mengubah bar terakhir, setiap indikator melanjutkan dari bar itu (dan beberapa bar sebelumnya yang dilihat rumusnya) alih-alih menghitung ulang seluruh seri. Jika bar yang diingatnya tidak lagi cocok, indikator kembali ke perhitungan ulang penuh, sehingga koreksi tidak pernah terlewat.

Sekitar 20 indikator punya pembaruan inkremental sendiri, dan 31 lainnya dibangun di atas basis yang melanjutkan titik demi titik. Indikator yang membaca garis indikator lain juga melanjutkan.

0 / 64 Diukur pada 100.000 bar dengan empat indikator: sekitar 0,001 ms per tick
04

Saat diperkecil: satu kolom per piksel

Saat bar lebih sempit dari satu piksel, menggambar masing-masing hanya membuang kerja yang tak terlihat mata. Candle, bar, high-low, dan volume digambar sebagai satu kolom per piksel, dengan open pertama, high tertinggi, low terendah, dan close terakhir dari bar di bawahnya. Garis dan area menyimpan sekitar dua titik per piksel (LTTB); garis dan band indikator menggambar satu rentang per kolom piksel.

bar terlihat
313
bar per piksel
0,39
bentuk digambar
313

Bar tidak pernah lebih sempit dari seperempat piksel CSS, jadi grafik berisi sejuta bar menampilkan paling banyak sekitar empat bar per piksel saat diperkecil sepenuhnya: sekitar 6.400 pada grafik 1.600 px. Menggeser melewati sisanya adalah yang diukur benchmark sejuta bar.

05

Dua kanvas, jadi hover nyaris tanpa biaya

Adegan (grid, seri, indikator, gambar, order) ada di satu kanvas; crosshair, legenda, label sumbu, dan hitung mundur ada di kanvas tipis di atasnya. Menggerakkan mouse hanya menggambar ulang kanvas atas. Pembaruan data menggambar ulang adegan sekali, di frame berikutnya.

Setiap kanvas berukuran penuh membebani browser dengan satu blend per frame, jadi mesin menjaga jumlahnya sedikit: kanvas belakang untuk gambar khusus 2D hanya ada selama ada yang membutuhkannya.

06

WebGL dengan merekam, bukan menulis ulang

Grafik menggambar dengan panggilan Canvas 2D. Saat WebGL aktif, panggilan itu masuk ke perekam yang mengubah path, isian, dan stroke menjadi perintah GPU dalam piksel perangkat. Apa pun yang tidak bisa digambar GPU dengan sama tetap di Canvas 2D, sesuai urutan, sehingga gambarnya sama dengan cara mana pun.

  • renderer: 'auto' meminta browser sebuah konteks tanpa peringatan performa besar, jadi mesin yang WebGL-nya akan berjalan di perangkat lunak tetap memakai Canvas 2D.
  • Paling banyak 8 grafik dalam satu halaman memegang konteks WebGL (setMaxWebGLCharts); sisanya menunggu dalam antrean dan menggambar dengan Canvas 2D.
  • Jika GPU melepas konteksnya, grafik tetap berjalan dengan Canvas 2D dan dibangun ulang di WebGL saat konteksnya kembali.
  • Candle dibulatkan ke piksel perangkat utuh dengan aturan yang sama di shader dan di CPU, jadi kedua perender menempatkannya pada piksel yang sama.
07

Dibuat untuk mesin yang lemah

  • Canvas 2D secara bawaanWebGL bersifat opsional sampai diukur di lebih banyak perangkat. Pada grafis terintegrasi dengan rasio piksel tinggi, yang menjadi hambatan adalah compositing browser sendiri, bukan penggambarannya.
  • Satu resize per frameEvent resize digabung menjadi satu per frame, dan ukuran yang tidak berubah tidak melakukan apa-apa.
  • Riwayat per halamanBar lama dimuat 500 sekaligus saat Anda menggulir mundur, satu permintaan pada satu waktu, dan menunda percobaan setelah gagal.
  • Tanpa kedipan loadingSelubung loading menunggu 200 ms, jadi pergantian simbol yang cepat tidak pernah berkedip.
  • Kecil untuk diunduhGrafik headless berukuran 176 KB setelah gzip, widget lengkap 315 KB, dan perender WebGL 18 KB lagi saat dipakai.
  • Papan yang diperbarui di tempatPapan harga bo-grid menggabungkan tick menjadi satu tambalan per frame dan hanya menggambar ulang sel yang berubah.
08

Yang masih kami perbaiki

Mesinnya belum selesai. Inilah biaya yang kami ketahui, dari kode sumbernya, dan inilah yang akan dikerjakan berikutnya.

  1. Indikator diperbarui di setiap pembaruan bar, bukan sekali per frame: feed yang mengirim banyak tick per frame menghitung ulang lebih sering daripada menggambar.
  2. Bar disimpan sebagai array objek. Penyimpanan kolumnar dari typed array akan memakai lebih sedikit memori dan ditelusuri lebih cepat pada riwayat yang sangat panjang.
  3. Semuanya berjalan di main thread. Worker untuk perhitungan indikator sudah ada di kode tetapi belum dihubungkan ke grafik.
  4. Heatmap kedalaman menyimpan snapshot-nya dalam array biasa dan memindai ulang setiap level untuk mencari maksimum di setiap frame; profil volume mengalokasikan bucket-nya di setiap gambar.
  5. Beberapa objek masih dialokasikan setiap frame (path, array kecil), yang memberi kerja pada garbage collector.
  6. Pada 2560×1400 dengan rasio piksel 2 di grafis terintegrasi, compositing saja memakan sekitar 23 ms, sehingga frame WebGL di sana rata-rata 29 ms.
Ukur di perangkat Anda →Halaman audit menjalankan adegan ini di browser Anda: sejuta bar di Canvas 2D dan WebGL, tick langsung pada 100.000 bar, dan papan 1.000 simbol.