Arquitectura

Cómo se mantiene rápido en un portátil corriente.

Las pantallas de trading pesan: historiales largos, ticks que no paran, muchos gráficos en una página. TradeCanvas hace solo el trabajo que necesita cada fotograma, en el hilo principal y sin código de terceros. Esta página muestra cómo, y dónde aún se está mejorando.

En desarrollo: el motor ya es lo bastante rápido para producción, pero todavía no tanto como lo será. La última sección enumera lo que sabemos que falta.

01

Paquetes pequeños, en una sola dirección

Cada paquete depende solo de los que tiene debajo. Nada ajeno a TradeCanvas se ejecuta en tu página: los componentes para frameworks reciben React, Vue o Svelte como dependencias peer, y el renderizador WebGL es un fragmento aparte que solo se carga cuando un gráfico lo pide.

0 dependencias de terceros en tiempo de ejecución

02

El recorrido de un tick

Un precio llega desde tu fuente de datos y alcanza la pantalla en un fotograma. Cada etapa hace solo la parte que cambió.

  1. 1Tu fuente de datosDataAdapterWebSocket, REST, un bróker: cualquier cosa con historial y un flujo en vivo.
  2. 2Gestor del flujoStreamManagerPrimero el historial, luego el directo. Las respuestas a una petición antigua se descartan; una fuente que se queda en silencio se detecta en menos de 30 s.
  3. 3Agregador de ticksTickAggregatorLos ticks entran en un búfer circular de 1,000 y se incorporan a la barra de su temporalidad.
  4. 4IndicadoresIndicatorEngineSe recalculan desde la última barra, no desde el inicio de la serie.
  5. 5ViewportViewportSolo se recorren las barras en pantalla; la escala de precios sale únicamente de ellas.
  6. 6Bucle de renderizadoRenderLoopLleguen los ticks que lleguen, la escena se dibuja como mucho una vez por fotograma de animación.
  7. 7Canvas 2D o WebGLRenderEngineDos canvas, y un canvas de GPU debajo cuando WebGL está activo.
03

Un tick cuesta una barra, no todo el historial

Cuando un tick cambia la última barra, cada indicador continúa desde esa barra (y las pocas anteriores que su fórmula consulta) en lugar de recalcular la serie. Si las barras que recuerda ya no coinciden, vuelve a un recálculo completo, así que nunca se pierde una corrección.

Unos 20 indicadores tienen su propia actualización incremental, y otros 31 se basan en una base que continúa punto a punto. Los indicadores que leen la línea de otro indicador también continúan.

0 / 64 Medido sobre 100,000 barras con cuatro indicadores: unos 0.001 ms por tick
04

Con el zoom alejado: una columna por píxel

Cuando las barras son más estrechas que un píxel, dibujar cada una desperdicia trabajo que el ojo no ve. Las velas, las barras, el máximo-mínimo y el volumen se dibujan como una columna por píxel, con la primera apertura, el máximo más alto, el mínimo más bajo y el último cierre de las barras que caen en ella. Las líneas y las áreas conservan unos dos puntos por píxel (LTTB); las líneas y bandas de los indicadores dibujan un tramo por columna de píxeles.

barras en pantalla
313
barras por píxel
0,39
formas dibujadas
313

Una barra nunca es más estrecha que un cuarto de píxel CSS, así que un gráfico con un millón de barras muestra como mucho unas cuatro barras por píxel con el zoom totalmente alejado: unas 6,400 en un gráfico de 1,600 px. Desplazarse por el resto es lo que mide el benchmark del millón de barras.

05

Dos canvas, para que pasar el ratón no cueste casi nada

La escena (cuadrícula, series, indicadores, dibujos, órdenes) vive en un canvas; la cruz, la leyenda, las etiquetas de los ejes y la cuenta atrás viven en un canvas fino encima. Mover el ratón solo vuelve a pintar el de arriba. Una actualización de datos vuelve a pintar la escena una vez, en el siguiente fotograma.

Cada canvas a tamaño completo le cuesta al navegador una composición por fotograma, así que el motor usa pocos: un canvas trasero para dibujos solo 2D existe únicamente mientras algo lo necesita.

06

WebGL grabando, no reescribiendo

El gráfico dibuja con llamadas de Canvas 2D. Con WebGL activo, esas llamadas van a un grabador que convierte trazados, rellenos y trazos en comandos de GPU en píxeles del dispositivo. Lo que la GPU no puede dibujar igual se queda en Canvas 2D, en orden, así que la imagen es la misma en ambos casos.

  • renderer: 'auto' pide al navegador un contexto sin advertencia de rendimiento grave, así que una máquina cuyo WebGL funcionaría por software se queda con Canvas 2D.
  • Como mucho 8 gráficos de una página tienen un contexto WebGL (setMaxWebGLCharts); los demás esperan en una cola y dibujan con Canvas 2D.
  • Si la GPU pierde el contexto, el gráfico sigue con Canvas 2D y se reconstruye en WebGL cuando el contexto vuelve.
  • Las velas se redondean a píxeles enteros del dispositivo con la misma regla en los shaders y en la CPU, así que ambos renderizadores las ponen en los mismos píxeles.
07

Hecho para máquinas modestas

  • Canvas 2D por defectoWebGL es opcional hasta que se haya medido en más dispositivos. En gráficos integrados con una densidad de píxeles alta, el cuello de botella es la composición del propio navegador, no el dibujo.
  • Un redimensionado por fotogramaLos eventos de cambio de tamaño se agrupan en uno por fotograma, y un tamaño que no cambió no hace nada.
  • Historial por páginasLas barras antiguas se cargan de 500 en 500 al desplazarte hacia atrás, una petición cada vez, esperando más tras un fallo.
  • Sin parpadeos de cargaEl velo de carga espera 200 ms, así que un cambio rápido de símbolo nunca parpadea.
  • Poco que descargarEl gráfico sin interfaz ocupa 176 KB con gzip, el widget completo 315 KB y el renderizador WebGL otros 18 KB cuando se usa.
  • Pizarras que se actualizan en su sitioLa pizarra de precios de bo-grid agrupa los ticks en un parche por fotograma y solo vuelve a pintar las celdas que cambiaron.
08

Lo que aún estamos mejorando

El motor no está terminado. Estos son los costes que conocemos, sacados del código fuente, y son lo próximo en lo que trabajar.

  1. Los indicadores se actualizan con cada actualización de barra, no una vez por fotograma: una fuente que envía muchos ticks por fotograma recalcula más a menudo de lo que dibuja.
  2. Las barras se guardan como un array de objetos. Un almacén por columnas con arrays tipados usaría menos memoria y se recorrería más rápido en historiales muy largos.
  3. Todo se ejecuta en el hilo principal. En el código existe un worker para los cálculos de indicadores, pero aún no está conectado al gráfico.
  4. El mapa de calor de profundidad guarda sus instantáneas en un array simple y vuelve a recorrer todos los niveles para buscar su máximo en cada fotograma; el perfil de volumen reserva sus cubetas en cada dibujo.
  5. Algunos objetos se siguen creando en cada fotograma (trazados, arrays pequeños), lo que da trabajo al recolector de basura.
  6. A 2560×1400 y una densidad de píxeles de 2 en gráficos integrados, solo la composición tarda unos 23 ms, así que ahí los fotogramas WebGL promedian 29 ms.
Mídelo en tu máquina →La auditoría ejecuta estas escenas en tu navegador: un millón de barras en Canvas 2D y WebGL, ticks en vivo sobre 100,000 barras y una pizarra de 1,000 símbolos.