Skip to main contentRetour en haut de la page

BLOG-POST MODULE

NebulaGraph : 1 million de points de données à 60 ips avec WebGL

19/02/2026

9 min de lecture

201 vues

NebulaGraph : rendu d'un million de points de données en temps réel avec WebGL

Essayez de visualiser un million de points de données dans une feuille de calcul ou même dans un tableau traditionnel. bibliothèque de graphiques. Vous obtiendrez l'un des deux résultats suivants :

  1. Il plante votre navigateur (plus de 50 Mo de nœuds DOM)
  2. Le rendu s'effectue en 30 secondes (l'utilisateur abandonne avant de voir la visualisation)

Les data scientists travaillent souvent avec des ensembles de données aussi volumineux : corrélations entre les cours des actions, matrices d'expression génique, intégrations de réseaux neuronaux. Mais la visualisation existante les outils ont atteint un maximum d'environ 100 000 points avant de s'étouffer.

C'est l'histoire de NebulaGraph, un moteur de visualisation basé sur WebGL que nous conçu pour gérer des ensembles de données massifs avec une interactivité en temps réel.

Le problème : le DOM ne peut pas évoluer

Une approche traditionnelle de la visualisation des données :

// Plotting 1 million points the naive way
const svg = d3.select("body").append("svg");
data.forEach((point) => {
  svg.append("circle")
    .attr("cx", point.x)
    .attr("cy", point.y)
    .attr("r", 2)
    .style("fill", colorScale(point.value));
});

Ce qui se produit?

  • Créer 1 000 000 de nœuds DOM
  • Le moteur CSS calcule la mise en page pour chacun (calculs de mise en page 1M)
  • Mémoire du navigateur : 50 à 100 Mo uniquement pour les cercles
  • Interaction (zoom, panoramique, survol) : Geler pendant plus de 2 secondes

Le goulot d'étranglement : Le DOM a été conçu pour les documents, pas pour des millions de personnes. objets graphiques. Chaque élément est un nœud complet qui peut avoir des événements auditeurs, styles, animations : une infrastructure lourde pour quelque chose qui n'est qu'un pixel.

Les data scientists :

  • Utiliser Python avec Matplotlib (pas d'interactivité, PNG statique)
  • Utilisez Plotly (interactif mais lent > 50 000 points)
  • Utiliser des outils spécialisés (Graphia pour la visualisation graphique, Tableau pour les informations commerciales)
  • Créez un WebGL personnalisé (nécessite une expertise graphique que la plupart n'ont pas)

Nous avons demandé : Pourrions-nous rendre WebGL accessible aux data scientists sans graphiques compétence?

La solution : le rendu accéléré par GPU

Nous avons construit NebulaGraph en trois couches :

Couche 1 : moteur de rendu WebGL avec shaders GLSL

Au lieu d'éléments DOM, chaque point est un triangle unique en WebGL, rendu par le GPU :

// vertex.glsl - Runs once per vertex (1M times, in parallel on GPU)
attribute vec3 position;
attribute float value;  // Data value for coloring
attribute float selected; // For highlighting

uniform mat4 projectionMatrix;
uniform mat4 viewMatrix;
uniform float time;

varying vec4 vColor;
varying float vValue;

void main() {
  // Position: transform to screen space
  gl_Position = projectionMatrix * viewMatrix * vec4(position, 1.0);
  
  // Size: responsive to zoom level
  gl_PointSize = mix(2.0, 8.0, float(selected));
  
  // Color: based on data value
  vColor = colorForValue(value);
  vValue = value;
}
// fragment.glsl - Runs once per pixel
varying vec4 vColor;
varying float vValue;

void main() {
  // Circular point shape (instead of square)
  float r = distance(gl_PointCoord, vec2(0.5));
  if (r > 0.5) discard; // Transparent outside circle
  
  gl_FragColor = vColor;
  gl_FragColor.a = 1.0 - smoothstep(0.4, 0.5, r); // Soft edges
}

Résultat : 1 million de points rendus en ~16 ms (60 ips) au lieu de 30 secondes.

Couche 2 : optimisation du transfert de données

Le goulot d'étranglement est passé du rendu au transfert de données :

// Traditional: Transfer JSON
const data = [
  { x: 0.1, y: 0.2, value: 0.5 },
  { x: 0.15, y: 0.25, value: 0.55 },
  // ... 999,998 more objects
  // Result: ~40MB of JSON
];

// NebulaGraph: Binary streaming
const floatBuffer = new Float32Array(1000000 * 3); // 12MB
let offset = 0;
data.forEach((point) => {
  floatBuffer[offset++] = point.x;
  floatBuffer[offset++] = point.y;
  floatBuffer[offset++] = point.value;
});

// Transfer via ArrayBuffer (native binary, 3x smaller than JSON)
const arrayBuffer = floatBuffer.buffer;

En utilisant binary Float32Arrays au lieu de JSON, nous avons réduit la taille du transfert de 40Mo à 12Mo (70% de réduction). Pour les scénarios impliquant beaucoup de réseau, cela signifiait 3-4 deuxième temps de chargement au lieu de plus de 30 secondes.

Couche 3 : API adaptée aux scientifiques de données

Nous avons éliminé la complexité de WebGL :

# Python data science workflow
import numpy as np
import pandas as pd
from nebulagraph import NebulaGraph

# Load dataset (e.g., gene expression data)
data = pd.read_csv("gene_expression.csv")
features = np.array(data[['gene1', 'gene2', 'gene3']].values)

# Project to 2D for visualization
from sklearn.decomposition import PCA
pca = PCA(n_components=2)
points_2d = pca.fit_transform(features)

# Create interactive visualization
viz = NebulaGraph(
  data=points_2d,
  values=data['expression_level'],
  color_by='expression_level',
  title='Gene Expression Landscape'
)
viz.show()

Dans les coulisses, NebulaGraph :

  • Sérialisé le tableau numpy au format binaire
  • Contexte WebGL ouvert dans Jupyter
  • Données binaires diffusées sur GPU
  • Rendu de plus de 1 million de points de manière interactive

Résultat : Les data scientists n'ont pas écrit WebGL. Ils ont écrit Python.**

Les résultats : démocratiser la visualisation des données

60 ips à 1 million de points

  • Outils traditionnels : Gelés à 20-30 points
  • Matplotlib : statique, pas d'interactivité
  • Plotly : 15-20 ips à 50 000 points, inutilisable à 100 000+
  • NebulaGraph : 60 ips stable à 1 million de points

Cela a permis une exploration interactive : zoomer, filtrer, survoler points individuels pour voir les valeurs.

Temps de chargement 80 % plus rapides

  • Transfert JSON (40 Mo) + rendu DOM : 30-45 secondes
  • Transfert binaire NebulaGraph + rendu GPU : 5 à 8 secondes
  • Pour les workflows itératifs (charger, explorer, modifier les paramètres, recharger), 20 minutes analyse → analyse de 5 minutes

Cas d'utilisation réels

Modélisation financière (corrélations boursières)

  • Visualisez plus de 5 000 actions × 2 000+ jours de bourse (10 millions de points)
  • Identifiez instantanément les clusters de corrélation en survolant
  • Zoomez pour comparer les chemins de stock individuels au sein du cluster

Analyse de l'expression génique

  • Tracer 20 000 gènes × 500 échantillons (10 millions de points)
  • Couleur par type de cellule, voir quels gènes co-expriment
  • Survolez les métadonnées génétiques (fonction, voie, association à la maladie)

Visualisation du réseau neuronal

  • Tracer 1 million d'activations de neurones à partir d'un modèle entraîné
  • Couleur par force d'activation
  • Identifier les représentations de fonctionnalités importantes

Analyse approfondie de l'architecture technique

Gestion de la mémoire

La mémoire GPU est limitée (généralement 1 à 4 Go de VRAM). Nous l’avons géré de manière agressive :

// Streaming data for massive datasets
class DataStreamBuffer {
  chunkSize = 100_000; // Process in chunks
  totalPoints: number;

  async *streamData(dataset: AsyncIterable<Point>) {
    let chunk = new Float32Array(this.chunkSize * 3);
    let offset = 0;

    for await (const point of dataset) {
      chunk[offset++] = point.x;
      chunk[offset++] = point.y;
      chunk[offset++] = point.value;

      if (offset === this.chunkSize * 3) {
        yield chunk; // Transfer chunk to GPU
        chunk = new Float32Array(this.chunkSize * 3);
        offset = 0;
      }
    }

    if (offset > 0) yield chunk.slice(0, offset); // Final partial chunk
  }
}

Cela nous permet de visualiser des ensembles de données plus grands que la mémoire GPU en streaming.

Performances des interactions

Le zoom/panoramique nécessitait de recalculer la matrice de projection à chaque image. Nous optimisé :

const camera = new THREE.PerspectiveCamera(75, aspect, 0.1, 1000);

const animate = () => {
  // Only recalculate matrix if camera moved
  if (camera.hasChanged) {
    camera.updateProjectionMatrix();

    // Reuse projection matrix across all shaders
    shader.uniforms.projectionMatrix.value = camera.projectionMatrix;
    shader.uniforms.viewMatrix.value = camera.matrixWorldInverse;

    camera.hasChanged = false;
  }

  renderer.render(scene, camera);
  requestAnimationFrame(animate);
};

Résultat : Le panoramique/zoom est resté à 60 ips même avec 1 million de points.

Cartographie des couleurs à grande échelle

Mapper efficacement des millions de valeurs en couleurs :

// Pre-computed color lookup table (texture)
uniform sampler2D colorMap; // 256×1 texture with color gradient
uniform float minValue;
uniform float maxValue;

void main() {
  // Normalize value to [0, 1]
  float normalized = (vValue - minValue) / (maxValue - minValue);
  
  // Look up color from texture (very fast)
  vec4 color = texture2D(colorMap, vec2(normalized, 0.5));
  
  gl_FragColor = color;
}

Cela a évité des calculs coûteux (logarithmes, interpolations en douceur) pour chaque fragment.

Ce que nous ferions différemment

1. Commencé avec les principes fondamentaux de la visualisation

Nous avons initialement créé une interface utilisateur riche en fonctionnalités avant de définir le rendu principal. Leçon : Des performances de base parfaites d'abord, les fonctionnalités ensuite.

2. Meilleure documentation pour les formats de données

Les data scientists ont eu du mal à savoir « quel format NebulaGraph accepte-t-il ? » Nous devrions ont fourni plus de modèles et d’exemples dès le départ.

3. Assistance mobile

La version initiale était réservée aux ordinateurs de bureau. Mobile WebGL a différentes contraintes (fenêtre d'affichage plus petite, VRAM limitée). Planifier cela dès le début aurait aidé.

Qui a besoin d'une visualisation accélérée par GPU

L'architecture NebulaGraph s'applique à :

  • Data Science : Exploration d'ensembles de données de grande dimension (clustering, sortie de réduction de dimensionnalité, matrices de corrélation)
  • Analyse de réseau : visualisation de structures graphiques avec plus de 100 000 nœuds
  • Données financières : surveillance simultanée de milliers de séries chronologiques
  • Informatique scientifique : simulations de particules, visualisations de dynamique des fluides
  • SIG/Mapping : rendu de millions de points de données géographiques

Premiers pas avec la visualisation WebGL

Si vous construisez :

  • Outils de visualisation de données performants
  • Tableaux de bord d'analyse en temps réel
  • Interfaces de calcul scientifique interactives
  • Systèmes gérant plus de 100 000 points de données

L'approche accélérée par GPU de NebulaGraph a fait ses preuves en production. Nous avons livré:

  • Rendu à 60 ips à plus de 1 million de points
  • Temps de chargement 80 % plus rapides par rapport aux approches traditionnelles
  • API Python adaptée aux data scientists

Explorer NebulaGraph

None

Articles connexes

None

Créer des outils de visualisation ou d'exploration de données hautes performances ? Parlons de votre architecture, ou abonnez-vous pour mises à jour sur les techniques de calcul GPU et de visualisation de données.

Synchronisation Newsletter