JetStream 3 : Le Benchmark Qui Reflète Enfin le Fonctionnement des Apps Web Modernes

JetStream 3 : Le Benchmark Qui Reflète Enfin le Fonctionnement des Apps Web Modernes

lschvn6 min de lecture

Les benchmarks ne sont utiles que s'ils conduisent à de vraies améliorations. Et un benchmark qui récompense les moteurs pour s'optimiser spécifiquement pour lui-même, plutôt que pour de vraies applications, devient contre-productif à terme.

C'est le problème central que JetStream 3 résout. Publié la semaine dernière par des ingénieurs de WebKit, Google et Mozilla, c'est la première révision majeure de la suite JetStream depuis 2019. Le web a considérablement changé en sept ans, et l'ancien benchmark commençait à montrer son âge de façons qui nuisaient finalement au progrès des performances.

Le Piège du Microbenchmark

Le JetStream 2 original notait WebAssembly en deux phases : une mesure de démarrage en itération unique et une mesure d'exécution plus longue. L'idée était raisonnable à l'époque, les premiers adopteurs de Wasm compilaient de grosses applications C et C++ (jeux, codecs) où les utilisateurs toléraient un coût de démarrage ponctuel pour un débit soutenu.

Mais les moteurs sont devenus rapides. Vraiment rapides. WebKit a optimisé le chemin d'instantiation Wasm de manière si agressive que pour les workloads plus petits, le temps de démarrage atteignait effectivement zéro milliseconde. Et comme JetStream 2 utilisait Date.now() pour le chronométrage, qui arrondit par défaut, les temps sous-millisecondes étaient enregistrés comme 0ms. La formule de score Score = 5000 / Temps produisait alors l'infini.

L'équipe a corrigé cela en clampant le score à 5000, mais c'était un signal clair : la méthodologie du benchmark avait dépassé son sujet. Un score "infini" ne vous dit rien d'utile sur la façon dont un moteur gère de vraies charges. Et un temps de démarrage nul dans un microbenchmark ignore ce qui se passe après l'instantiation, le vrai travail que fait votre application.

Un Scoring Unifié pour Wasm

JetStream 3 remplace le modèle divisé démarrage/runtime par la même méthodologie utilisée pour les benchmarks JavaScript. Chaque workload Wasm s'exécute maintenant sur plusieurs itérations, capturant :

  • Première itération: compilation et configuration initiale
  • Itérations du cas pire: à-coups, pauses GC et pics de tiering
  • Itérations du cas moyen: débit soutenu

Le tout est moyenné géométriquement en un score de sous-test unique, qui alimente la moyenne géométrique du benchmark complet. Les moteurs sont maintenant incités à optimiser le cycle de vie entier d'une instance Wasm, pas seulement l'instantiation.

Langages Réels, Chaînes d'outils Réelles

Les workloads Wasm de JetStream 3 sont compilés depuis cinq langages sources : C++, C#, Dart, Java et Kotlin. Cela reflète comment Wasm est réellement utilisé en production, pas seulement les moteurs de jeu C++, mais aussi Dart et Kotlin via WasmGC (utilisés par les frameworks web modernes comme Flutter), et Rust pour les modules critiques.

Les nouveaux workloads exercent des fonctionnalités Wasm à peine effleurées par JetStream 2 :

  • WasmGC: allocations mémoire à ramasse-miettes (structures, tableaux) permettant des motifs idiomatiques des langages de haut niveau
  • SIMD: une instruction, plusieurs données pour le traitement parallèle
  • Exception Handling: levée et rattrape structurée d'exceptions

La couverture JavaScript a aussi été mise à jour : Promises et fonctions async, fonctionnalités RegExp modernes, champs de classe publics et privés. Plusieurs workloads asm.js ont été supprimés, la technologie a été supplantée par WebAssembly.

L'Ingénierie Derrière les Gains de WebKit

L'équipe WebKit a publié une analyse détaillée de leurs optimisations JavaScriptCore pour JetStream 3. Les résultats sont substantiels :

Inlining des allocations GC : les programmes WasmGC créent des millions de petits objets. L'implémentation JSC originale appelait une fonction C++ pour chaque allocation. Deux changements ont livré ~40% d'amélioration sur les sous-tests WasmGC :

  1. Changement du layout des objets pour que structures et tableaux stockent leurs données inline après l'en-tête dans une allocation unique, éliminant la deuxième allocation et l'indirection de pointeur
  2. Inlining du chemin rapide d'allocation directement dans le code machine généré, une courte séquence d'instructions qui incrémente un pointeur, écrit l'en-tête et retourne sans quitter le code généré

Inlining du display de type : les langages WasmGC s'appuient fortement sur les vérifications de type runtime (casts, tests instanceof, appels de fonction indirects). WebKit a implémenté l'algorithme de display de type de Cohen et l'a inliné dans ses deux compilateurs, baseline (BBQ) et optimiseur (OMG). Ils ont aussi intégré les six premières entrées du display directement dans chaque enregistrement de type pour que les hiérarchies peu profondes nécessitent aucune indirection de pointeur et restent dans une seule ligne de cache.

Élimination du coût des destructeurs GC : précédemment, chaque objet WasmGC tenait une référence à sa définition de type et exécutait un destructeur lors de sa destruction, ce qui devait décrémenter le compteur de référence sous un verrou global. Restructurer l'information de type pour utiliser le mécanisme Structure existant du ramasse-miettes a éliminé les destructeurs entièrement, livrant encore ~40% sur le sous-test dart-flute-wasm.

Pourquoi Cela Compte pour les Développeurs JavaScript

Les benchmarks de navigateur ressemblent à de la trivia pour ingénieurs de navigateur, mais ils ont des conséquences pratiques directes. Quand les moteurs s'optimisent pour des benchmarks, toutes les applications web bénéficient, les optimisations sont réelles, les workloads ne sont qu'un proxy.

Le déplacement de JetStream 3 des microbenchmarks vers des workloads plus grands et plus longs signifie que les optimisations poursuivies par les moteurs seront celles qui comptent en production. Une amélioration de 40% sur un sous-test WasmGC signifie que les apps web Flutter, les outils Kotlin-vers-Wasm et toute application utilisant Wasm pour des tâches coûteuses tourneront plus vite dans Safari.

La collaboration entre les trois moteurs majeurs est aussi notable. JetStream 3 utilise un modèle de gouvernance ouvert, avec des contributions regroupées dans un dépôt GitHub partagé. L'objectif est un benchmark que tous les moteurs ont intérêt à optimiser honnêtement, ce qui le rend finalement utile aux développeurs.

JetStream 3 est disponible sur browserbench.org.

Questions fréquentes

Articles connexes

Plus de couverture avec des sujets et tags en commun.

Bun vs Node vs Deno en 2026 : Le Duel des Runtimes Que Personne N'a Demandé (Mais Que Tout Le Monde Fait)
runtimes

Bun vs Node vs Deno en 2026 : Le Duel des Runtimes Que Personne N'a Demandé (Mais Que Tout Le Monde Fait)

Trois runtimes JavaScript. Trois philosophies différentes. Des benchmarks indépendants à travers le throughput HTTP, les cold starts et la performance asynchrone racontent une histoire plus claire que le marketing ne pourrait jamais le faire. Voici l'analyse brutale et honnête pour les développeurs qui choisissent leur prochaine plateforme JS côté serveur.
Deno 2.9 livre un démarrage à froid 1,98x plus rapide, 2,2 à 3,1x moins de RSS en charge, le minimum-release-age npm activé par défaut, la politique de confiance no-downgrade et les tests par snapshot natifs
runtimes

Deno 2.9 livre un démarrage à froid 1,98x plus rapide, 2,2 à 3,1x moins de RSS en charge, le minimum-release-age npm activé par défaut, la politique de confiance no-downgrade et les tests par snapshot natifs

Deno 2.9 (Bartek Iwańczuk, publié le 2026-06-25 sur deno.com/blog/v2.9) est la plus grande release Deno du cycle. Le démarrage à froid passe de 34,2 ms à 17,3 ms (1,98x), le pic RSS sur la charge realworld de Deno.serve baisse d'un facteur 2,2 (142 Mo → 64 Mo) et de 3,1x sur les corps de 1 Mio (197 Mo → 63 Mo), et le débit Deno.serve grimpe de 1,27x en realworld (56,8k → 72,4k req/s), 1,11x en plaintext, et 1,18x sur les corps de 1 Mio. Durcissement supply chain : le npm minimum-release-age est activé par défaut avec une fenêtre de 24h (PR #35458), et une nouvelle politique de confiance opt-in no-downgrade (PR #34927) refuse de résoudre toute version dont la preuve de confiance (publication échelonnée, trusted publishing, attestation de provenance) est plus faible que la meilleure preuve sur toute version précédemment publiée du même paquet. Parité du runner de tests : t.assertSnapshot() natif (#35139), Deno.test.each (#34938), --shard pour fan-out CI (#35057), retry et repeats (#35053), sélection change-aware --changed et --related (#35199), et seuils de coverage (#35056). Interop lockfile : deno install seed deno.lock depuis package-lock.json, pnpm-lock.yaml, yarn.lock ou bun.lock (#34296, #35330, #35346, #35350, #35394), pnpm-workspace.yaml auto-migre vers deno.json / package.json (#34993), et les marqueurs de conflit de fusion git dans deno.lock se résolvent automatiquement (#34726). Plus : deno desktop sort de l'expérimental (PR #33441 du 16 juin), sous-commandes deno link / deno unlink / deno list / deno watch, --unsafe-proto stable (#34738), Web Locks API (#31166), Happy Eyeballs v2 (RFC 8305) (#31726), navigator.userAgentData (#34743), la proposition WebCrypto Modern Algorithms (ML-KEM, ML-DSA, SLH-DSA, ChaCha20-Poly1305, famille SHA-3, KMAC, Argon2) (#34447, #34448, #34914, #35223), compat Node 26.3.0 (#34746, #34747), Node-API v10 (#35270), et imports de modules CSS sous --unstable-raw-imports (#35093). Plus de 165 PRs mergent dans ce cycle.
Node.js 24.18.0 'Krypton' LTS : Buffer.poolSize à 64 Kio, TurboSHAKE et KangarooTwelve dans Web Crypto, et http.writeInformation pour les codes 1xx arbitraires
runtimes

Node.js 24.18.0 'Krypton' LTS : Buffer.poolSize à 64 Kio, TurboSHAKE et KangarooTwelve dans Web Crypto, et http.writeInformation pour les codes 1xx arbitraires

Node.js 24.18.0 'Krypton' (LTS), publié le 2026-06-23, embarque la valeur par défaut Buffer.poolSize à 64 Kio déjà livrée sur Current par la 26.3.0, ajoute à Web Cryptography les algorithmes TurboSHAKE et KangarooTwelve de la RFC 9861 (PR #62183, 1 521 ajouts, 13 fichiers), expose http.writeInformation pour envoyer des codes 1xx arbitraires (PR #63155, 306 ajouts, 7 fichiers), rend accessible au runtime JS le démarrage de la couverture précise de V8 (commit 8c989ec4a3), ajoute l'import-export JWK pour les types de clés post-quantiques ML-KEM et SLH-DSA (PR #62706, 842 ajouts, 39 fichiers), câble en BoringSSL ML-DSA, ML-KEM, ChaCha20-Poly1305 et AES-KW pour Web Crypto (PR #63255), durcit WebCrypto contre la pollution de prototype (PR #63363), aligne les noms d'arguments de clé de crypto.diffieHellman et accepte des données de clé en entrée (PR #62527), annule le comportement 'stream: noop pause/resume on destroyed streams' de la 24.16.0 (PR #63834), et publie dans la foulée un correctif minimal 22.23.1 qui revient sur un changement d'agent http introduit dans la release de sécurité 06-18 et qui rattachait des listeners de stream sur les sockets idle.

Commentaires

Connexion Connectez-vous pour participer à la conversation.

Pas encore de commentaires. Soyez le premier à partager vos pensées.