สถาปัตยกรรม

เร็วได้อย่างไรบนแล็ปท็อปธรรมดา

หน้าจอเทรดนั้นหนัก: ประวัติยาว ติกที่ไม่เคยหยุด กราฟหลายตัวในหน้าเดียว TradeCanvas ทำงานเท่าที่แต่ละเฟรมต้องการเท่านั้น บนเธรดหลัก โดยไม่มีโค้ดจากภายนอก หน้านี้แสดงว่าทำอย่างไร และตรงไหนที่ยังปรับปรุงอยู่

อยู่ระหว่างพัฒนา: เอนจินเร็วพอจะใช้งานจริงได้แล้ว แต่ยังไม่เร็วเท่าที่จะเป็นในอนาคต หัวข้อสุดท้ายแสดงสิ่งที่เรารู้ว่ายังเหลืออยู่

01

แพ็กเกจเล็ก ทิศทางเดียว

แต่ละแพ็กเกจพึ่งพาเฉพาะแพ็กเกจที่อยู่ข้างล่างเท่านั้น ไม่มีโค้ดนอก TradeCanvas ทำงานในหน้าของคุณ คอมโพเนนต์สำหรับเฟรมเวิร์กใช้ React, Vue หรือ Svelte เป็น peer dependency และตัวเรนเดอร์ WebGL เป็นชังก์แยกที่โหลดเฉพาะเมื่อกราฟเรียกใช้

0 ไลบรารีภายนอกที่ต้องพึ่งพาขณะรัน

02

เส้นทางของหนึ่งติก

ราคามาจากฟีดของคุณและไปถึงหน้าจอภายในเฟรมเดียว แต่ละขั้นทำเฉพาะส่วนที่เปลี่ยน

  1. 1ฟีดของคุณDataAdapterWebSocket, REST, โบรกเกอร์: อะไรก็ได้ที่มีประวัติและสตรีม
  2. 2ตัวจัดการสตรีมStreamManagerโหลดประวัติก่อน แล้วจึงต่อข้อมูลสด คำตอบจากคำขอเก่าจะถูกทิ้ง และฟีดที่เงียบไปจะถูกตรวจพบภายใน 30 วินาที
  3. 3ตัวรวมติกTickAggregatorติกเข้าบัฟเฟอร์วงแหวนขนาด 1,000 แล้วถูกรวมเข้าแท่งของกรอบเวลานั้น
  4. 4อินดิเคเตอร์IndicatorEngineคำนวณใหม่จากแท่งสุดท้าย ไม่ใช่จากต้นชุดข้อมูล
  5. 5วิวพอร์ตViewportไล่เฉพาะแท่งที่อยู่บนจอ และสเกลราคาคำนวณจากแท่งเหล่านั้นเท่านั้น
  6. 6ลูปการเรนเดอร์RenderLoopไม่ว่าติกจะเข้ามากี่ติก ฉากจะถูกวาดอย่างมากหนึ่งครั้งต่อเฟรมแอนิเมชัน
  7. 7Canvas 2D หรือ WebGLRenderEngineแคนวาสสองชั้น และมีแคนวาส GPU อยู่ข้างใต้เมื่อเปิด WebGL
03

หนึ่งติกใช้ต้นทุนแค่แท่งเดียว ไม่ใช่ทั้งประวัติ

เมื่อติกเปลี่ยนแท่งสุดท้าย อินดิเคเตอร์แต่ละตัวจะคำนวณต่อจากแท่งนั้น (และแท่งก่อนหน้าไม่กี่แท่งที่สูตรต้องย้อนดู) แทนการคำนวณทั้งชุดใหม่ หากแท่งที่จำไว้ไม่ตรงกันแล้ว ก็จะกลับไปคำนวณใหม่ทั้งหมด จึงไม่พลาดการแก้ไขข้อมูล

อินดิเคเตอร์ราว 20 ตัวมีการอัปเดตแบบคำนวณเพิ่มของตัวเอง และอีก 31 ตัวสร้างบนฐานที่คำนวณต่อทีละจุด อินดิเคเตอร์ที่อ่านเส้นของอินดิเคเตอร์อื่นก็คำนวณต่อได้เช่นกัน

0 / 64 วัดบน 100,000 แท่งพร้อมอินดิเคเตอร์สี่ตัว: ประมาณ 0.001 ms ต่อติก
04

เมื่อซูมออก: หนึ่งคอลัมน์ต่อพิกเซล

เมื่อแท่งแคบกว่าหนึ่งพิกเซล การวาดทีละแท่งคือการทำงานที่ตามองไม่เห็น แท่งเทียน บาร์ high-low และวอลุ่มจะถูกวาดเป็นหนึ่งคอลัมน์ต่อพิกเซล โดยใช้ราคาเปิดแรก ราคาสูงสุดที่สูงที่สุด ราคาต่ำสุดที่ต่ำที่สุด และราคาปิดสุดท้ายของแท่งที่อยู่ใต้พิกเซลนั้น เส้นและพื้นที่เก็บไว้ราวสองจุดต่อพิกเซล (LTTB) ส่วนเส้นและแบนด์ของอินดิเคเตอร์วาดหนึ่งช่วงต่อคอลัมน์พิกเซล

แท่งที่มองเห็น
313
แท่งต่อพิกเซล
0.39
รูปที่วาด
313

แท่งไม่มีวันแคบกว่าหนึ่งในสี่ของพิกเซล CSS กราฟที่มีล้านแท่งจึงแสดงได้มากสุดราวสี่แท่งต่อพิกเซลเมื่อซูมออกจนสุด: ประมาณ 6,400 แท่งบนกราฟกว้าง 1,600 px การเลื่อนผ่านส่วนที่เหลือคือสิ่งที่เบนช์มาร์กล้านแท่งวัด

05

แคนวาสสองชั้น การชี้เมาส์จึงแทบไม่มีต้นทุน

ฉาก (กริด ซีรีส์ อินดิเคเตอร์ ภาพวาด คำสั่ง) อยู่บนแคนวาสหนึ่ง ส่วนเป้าเล็ง คำอธิบายกราฟ ป้ายแกน และตัวนับถอยหลังอยู่บนแคนวาสบาง ๆ ด้านบน การขยับเมาส์จะวาดใหม่เฉพาะชั้นบน การอัปเดตข้อมูลจะวาดฉากใหม่หนึ่งครั้งในเฟรมถัดไป

แคนวาสขนาดเต็มทุกชั้นทำให้เบราว์เซอร์ต้องผสมภาพหนึ่งครั้งต่อเฟรม เอนจินจึงใช้ให้น้อย: แคนวาสด้านหลังสำหรับการวาดแบบ 2D เท่านั้นมีอยู่เฉพาะตอนที่มีสิ่งต้องใช้

06

WebGL ด้วยการบันทึก ไม่ใช่การเขียนใหม่

กราฟวาดด้วยคำสั่งของ Canvas 2D เมื่อเปิด WebGL คำสั่งเหล่านั้นจะไปยังตัวบันทึกที่แปลงพาธ การระบาย และเส้นขอบเป็นคำสั่ง GPU ในหน่วยพิกเซลของอุปกรณ์ สิ่งที่ GPU วาดให้เหมือนกันไม่ได้จะคงอยู่บน Canvas 2D ตามลำดับ ภาพจึงเหมือนกันไม่ว่าจะใช้แบบไหน

  • renderer: 'auto' ขอคอนเท็กซ์จากเบราว์เซอร์แบบที่ไม่มีข้อควรระวังด้านประสิทธิภาพร้ายแรง เครื่องที่ WebGL จะรันด้วยซอฟต์แวร์จึงยังใช้ Canvas 2D
  • ในหน้าเดียวมีกราฟถือคอนเท็กซ์ WebGL ได้สูงสุด 8 กราฟ (setMaxWebGLCharts) ที่เหลือรอคิวและวาดด้วย Canvas 2D
  • หาก GPU ทิ้งคอนเท็กซ์ กราฟจะทำงานต่อด้วย Canvas 2D และสร้างใหม่บน WebGL เมื่อคอนเท็กซ์กลับมา
  • แท่งเทียนถูกปัดเป็นพิกเซลอุปกรณ์เต็มด้วยกฎเดียวกันทั้งในเชเดอร์และบน CPU ตัวเรนเดอร์ทั้งสองจึงวางแท่งไว้บนพิกเซลเดียวกัน
07

สร้างมาเพื่อเครื่องสเปกต่ำ

  • Canvas 2D เป็นค่าเริ่มต้นWebGL ต้องเปิดเองจนกว่าจะได้วัดผลบนอุปกรณ์มากขึ้น บนกราฟิกในตัวที่อัตราส่วนพิกเซลสูง คอขวดคือการผสมภาพของเบราว์เซอร์เอง ไม่ใช่การวาด
  • ปรับขนาดหนึ่งครั้งต่อเฟรมอีเวนต์ปรับขนาดถูกรวมเป็นหนึ่งครั้งต่อเฟรม และขนาดที่ไม่เปลี่ยนจะไม่ทำอะไรเลย
  • ประวัติแบบแบ่งหน้าแท่งเก่าโหลดครั้งละ 500 แท่งเมื่อเลื่อนย้อนหลัง ทีละคำขอ และเว้นระยะก่อนลองใหม่หลังล้มเหลว
  • ไม่มีการกะพริบขณะโหลดม่านโหลดรอ 200 ms การสลับสัญลักษณ์ที่เร็วจึงไม่กะพริบ
  • ดาวน์โหลดเล็กกราฟแบบ headless มีขนาด 176 KB หลัง gzip วิดเจ็ตครบชุด 315 KB และตัวเรนเดอร์ WebGL อีก 18 KB เมื่อใช้
  • กระดานที่อัปเดตอยู่กับที่กระดานราคา bo-grid รวมติกเป็นแพตช์เดียวต่อเฟรม และวาดใหม่เฉพาะเซลล์ที่เปลี่ยน
08

สิ่งที่เรายังปรับปรุงอยู่

เอนจินยังไม่เสร็จสมบูรณ์ นี่คือต้นทุนที่เรารู้จากซอร์สโค้ด และเป็นสิ่งถัดไปที่จะทำ

  1. อินดิเคเตอร์อัปเดตทุกครั้งที่แท่งอัปเดต ไม่ใช่ครั้งเดียวต่อเฟรม ฟีดที่ส่งหลายติกต่อเฟรมจึงคำนวณใหม่บ่อยกว่าที่วาด
  2. แท่งถูกเก็บเป็นอาร์เรย์ของออบเจ็กต์ การเก็บแบบคอลัมน์ด้วย typed array จะใช้หน่วยความจำน้อยกว่าและไล่ได้เร็วกว่าบนประวัติที่ยาวมาก
  3. ทุกอย่างรันบนเธรดหลัก มี worker สำหรับคำนวณอินดิเคเตอร์อยู่ในโค้ดแล้ว แต่ยังไม่ได้เชื่อมเข้ากับกราฟ
  4. ฮีตแมปความลึกเก็บสแนปช็อตในอาร์เรย์ธรรมดาและสแกนทุกระดับใหม่เพื่อหาค่าสูงสุดในทุกเฟรม ส่วนวอลุ่มโปรไฟล์จัดสรรบักเก็ตใหม่ทุกครั้งที่วาด
  5. ยังมีบางออบเจ็กต์ที่ถูกจัดสรรทุกเฟรม (พาธ อาร์เรย์เล็ก ๆ) ซึ่งเพิ่มงานให้ตัวเก็บขยะ
  6. ที่ 2560×1400 และอัตราส่วนพิกเซล 2 บนกราฟิกในตัว การผสมภาพอย่างเดียวใช้ราว 23 ms เฟรม WebGL ที่นั่นจึงเฉลี่ย 29 ms
วัดผลบนเครื่องของคุณ →หน้าการตรวจสอบจะรันฉากเหล่านี้ในเบราว์เซอร์ของคุณ: ล้านแท่งบน Canvas 2D และ WebGL ติกสดบน 100,000 แท่ง และกระดาน 1,000 สัญลักษณ์