# Benchmarks

Le même travail, le même trafic. Explorez les mesures enregistrées, comprenez la méthode et reproduisez chaque test.

## Ce qui a été mesuré

Huit adaptateurs, quatre charges et des cibles communes de 100 et 1 000 requêtes/s : 192 mesures. Chaque mesure démarre un processus de production neuf : vérification de la réponse, 1,5 s de référence, 15 s d’échauffement, 6 s de stabilisation, 60 s de charge et 30 s de récupération. Les trois répétitions sont conservées. Les adaptateurs ont été mesurés l’un après l’autre. Le contenu des réponses réussies est vérifié avant et après la charge ; le générateur demande jusqu’à 32 connexions.

Total5 utilise un NEWSCHEMA compact et un contrôleur direct pour valider le JSON ; les petites réponses JSON sont renvoyées directement par le contrôleur. La comparaison contient des scénarios complets enregistrés, jamais le meilleur résultat chiffré. Meteor utilise son bundle de production côté serveur uniquement ; NestJS son adaptateur Express. Les validateurs natifs couvrent le contenu mesuré ; le rejet et la conversion peuvent différer d’un framework à l’autre.

## Environnement

Hôte Linux x64 partagé, Intel Core i7-6700. Chaque conteneur de mesure était limité à 2 CPU logiques et 2 GiB, sans réseau externe ni port publié. Le générateur et la dépendance simulée partageaient le conteneur. Node 24.14.1 a servi pour Total4, Express, Fastify et Node http ; Node 24.15.0 pour Total5, Meteor, AdonisJS et NestJS, en accord avec le runtime du bundle Meteor. Les versions du runtime sont indiquées à côté de chaque framework : les différences ne peuvent pas être attribuées au seul framework. Les services de production sont restés sur l’hôte.

## Ce que ces résultats ne démontrent pas

Il s’agit d’une expérience à débit fixe et en boucle fermée, pas d’un classement du débit maximal. Atteindre la cible configurée ne démontre pas comment un adaptateur gère des arrivées indépendantes ou la saturation. Une certaine limitation du CPU par quota a eu lieu ; elle est consignée dans la revue. Trois courtes répétitions ne démontrent ni la stabilité à long terme, ni l’absence de fuites de mémoire, ni la sécurité en production, ni le comportement de Flow, ni l’usage mémoire sur Raspberry Pi. Le ramasse-miettes n’a pas été forcé. Le RSS peut rester alloué après la fin du trafic.

Une cellule est saine lorsque les requêtes terminées atteignent au moins 95 % du débit cible × la durée du générateur, que le p99 est d’au plus 250 ms et que les nombres d’erreurs, de délais dépassés et de réponses non 2xx sont nuls. Ce seuil n’affirme pas une capacité maximale égale.

## Comment lire le résultat

Comparez la même charge au même rythme cible et lisez ensemble la latence, le CPU, la mémoire, les erreurs et la couverture de la cible. La version du runtime compte aussi. Ces mesures ne démontrent pas qu’un framework consomme toujours moins de ressources. Total.js 4 et 5 sont présentés séparément ; chacun utilise son implémentation et son runtime enregistrés.

## Comparaison complète des frameworks

Terminée: 2026-10-18T13:56:39.143Z; Node v24.14.1 / v24.15.0 (see runtimeByFramework).

| Framework | Node | Workload | req/s | Healthy | RSS baseline MiB | RSS load MiB | RSS recovery MiB | CPU % | p99 ms |
|---|---|---|---:|---|---|---|---|---|---|
| Total.js 4 | v24.14.1 | hello | 100 | 3/3 | 61.2 [61.0–61.2] | 89.8 [87.0–90.7] | 91.0 [88.3–105.4] | 1.7 [1.7–1.7] | 5.0 [5.0–6.0] |
| Express | v24.14.1 | hello | 100 | 3/3 | 74.2 [74.1–74.3] | 90.7 [90.4–91.5] | 90.8 [90.3–91.5] | 1.9 [1.9–2.0] | 7.0 [6.0–7.0] |
| Fastify | v24.14.1 | hello | 100 | 3/3 | 76.0 [75.5–76.0] | 91.3 [91.0–91.9] | 91.4 [91.0–91.9] | 1.4 [1.4–1.4] | 5.0 [5.0–5.0] |
| Node.js http | v24.14.1 | hello | 100 | 3/3 | 50.2 [50.2–50.3] | 68.8 [66.6–69.2] | 69.2 [67.0–69.5] | 1.4 [1.4–1.4] | 5.0 [5.0–5.0] |
| Total.js 4 | v24.14.1 | catalog | 100 | 3/3 | 61.3 [61.1–62.1] | 104.6 [94.9–106.2] | 106.4 [105.1–108.6] | 2.1 [2.1–2.1] | 8.0 [8.0–8.0] |
| Express | v24.14.1 | catalog | 100 | 3/3 | 74.1 [74.0–74.1] | 92.0 [91.9–94.5] | 92.3 [91.9–94.5] | 2.3 [2.3–2.4] | 9.0 [9.0–9.0] |
| Fastify | v24.14.1 | catalog | 100 | 3/3 | 75.8 [75.2–75.8] | 93.2 [92.6–93.4] | 93.4 [93.1–93.5] | 1.9 [1.8–1.9] | 8.0 [7.0–8.0] |
| Node.js http | v24.14.1 | catalog | 100 | 3/3 | 50.2 [50.1–50.3] | 66.0 [65.9–68.4] | 66.3 [66.1–68.7] | 1.8 [1.8–1.8] | 6.0 [6.0–7.0] |
| Total.js 4 | v24.14.1 | validate | 100 | 3/3 | 61.4 [61.3–61.6] | 96.4 [95.4–100.2] | 100.8 [98.3–101.9] | 2.0 [2.0–2.2] | 7.0 [6.0–8.0] |
| Express | v24.14.1 | validate | 100 | 3/3 | 74.0 [74.0–75.0] | 98.5 [94.4–99.0] | 98.6 [94.4–99.3] | 2.3 [2.2–2.3] | 9.0 [8.0–9.0] |
| Fastify | v24.14.1 | validate | 100 | 3/3 | 75.7 [75.6–76.0] | 97.3 [96.6–98.0] | 97.7 [96.2–98.0] | 1.7 [1.6–1.7] | 6.0 [5.0–6.0] |
| Node.js http | v24.14.1 | validate | 100 | 3/3 | 50.3 [50.2–50.5] | 71.6 [71.4–72.2] | 71.8 [71.4–72.6] | 1.7 [1.7–1.7] | 6.0 [6.0–6.0] |
| Total.js 4 | v24.14.1 | upstream | 100 | 3/3 | 67.2 [66.8–67.9] | 167.8 [166.7–169.5] | 175.3 [169.6–176.7] | 5.7 [5.7–5.8] | 19.0 [19.0–19.0] |
| Express | v24.14.1 | upstream | 100 | 3/3 | 80.2 [79.8–80.2] | 171.3 [171.2–171.4] | 173.7 [172.9–173.7] | 6.0 [5.7–6.2] | 20.0 [19.0–20.0] |
| Fastify | v24.14.1 | upstream | 100 | 3/3 | 80.3 [80.1–80.7] | 165.9 [165.8–168.4] | 168.7 [168.3–173.1] | 5.6 [4.8–5.8] | 20.0 [19.0–20.0] |
| Node.js http | v24.14.1 | upstream | 100 | 3/3 | 60.8 [60.7–61.6] | 161.9 [158.4–172.1] | 164.2 [159.9–174.2] | 5.5 [5.2–5.5] | 18.0 [18.0–19.0] |
| Total.js 4 | v24.14.1 | hello | 1000 | 3/3 | 61.0 [60.9–61.1] | 159.3 [150.1–167.0] | 174.3 [172.9–174.5] | 7.9 [7.8–8.2] | 5.0 [5.0–5.0] |
| Express | v24.14.1 | hello | 1000 | 3/3 | 74.1 [73.9–74.2] | 90.8 [90.4–91.5] | 90.9 [90.5–91.6] | 7.3 [7.1–7.6] | 5.0 [4.0–5.0] |
| Fastify | v24.14.1 | hello | 1000 | 3/3 | 75.9 [75.4–76.8] | 91.2 [91.0–91.6] | 91.2 [91.0–91.6] | 4.9 [4.9–5.0] | 3.0 [3.0–3.0] |
| Node.js http | v24.14.1 | hello | 1000 | 3/3 | 50.2 [50.2–50.2] | 67.9 [67.5–69.8] | 67.9 [67.7–70.0] | 4.4 [4.4–4.5] | 3.0 [3.0–3.0] |
| Total.js 4 | v24.14.1 | catalog | 1000 | 3/3 | 61.2 [61.0–61.3] | 179.5 [179.5–180.0] | 179.9 [179.4–180.2] | 9.9 [9.9–9.9] | 13.0 [6.0–18.0] |
| Express | v24.14.1 | catalog | 1000 | 3/3 | 73.8 [73.7–74.0] | 91.8 [91.1–92.9] | 91.8 [91.3–92.9] | 9.5 [9.4–9.8] | 5.0 [5.0–11.0] |
| Fastify | v24.14.1 | catalog | 1000 | 3/3 | 75.7 [75.3–75.9] | 94.1 [93.8–94.2] | 94.1 [93.8–94.2] | 7.1 [7.1–7.1] | 5.0 [5.0–5.0] |
| Node.js http | v24.14.1 | catalog | 1000 | 3/3 | 50.2 [50.1–50.2] | 72.6 [71.2–72.9] | 72.6 [71.2–73.2] | 7.3 [7.2–7.4] | 5.0 [4.0–5.0] |
| Total.js 4 | v24.14.1 | validate | 1000 | 3/3 | 62.2 [61.6–62.5] | 171.9 [170.7–172.7] | 175.7 [175.3–176.6] | 9.7 [9.5–10.0] | 6.0 [6.0–6.0] |
| Express | v24.14.1 | validate | 1000 | 3/3 | 74.4 [74.2–74.6] | 98.8 [98.2–99.7] | 98.8 [98.2–99.7] | 8.9 [8.9–9.2] | 8.0 [6.0–21.0] |
| Fastify | v24.14.1 | validate | 1000 | 3/3 | 75.9 [75.7–76.4] | 98.1 [98.1–98.4] | 98.1 [98.1–98.5] | 6.3 [6.2–6.3] | 5.0 [4.0–12.0] |
| Node.js http | v24.14.1 | validate | 1000 | 3/3 | 50.2 [50.2–50.4] | 71.0 [70.4–72.6] | 74.9 [74.2–76.4] | 6.2 [6.0–6.3] | 4.0 [4.0–13.0] |
| Total.js 4 | v24.14.1 | upstream | 1000 | 3/3 | 67.8 [67.8–67.8] | 202.3 [201.4–204.1] | 203.0 [201.6–204.7] | 36.3 [34.7–36.6] | 16.0 [15.0–16.0] |
| Express | v24.14.1 | upstream | 1000 | 3/3 | 80.7 [79.7–80.9] | 178.7 [177.6–187.7] | 182.6 [181.9–190.4] | 35.0 [32.6–35.4] | 15.0 [15.0–16.0] |
| Fastify | v24.14.1 | upstream | 1000 | 3/3 | 79.3 [79.3–79.3] | 194.0 [179.4–194.6] | 194.0 [179.6–194.9] | 33.8 [27.7–34.2] | 15.0 [14.0–15.0] |
| Node.js http | v24.14.1 | upstream | 1000 | 3/3 | 60.9 [60.7–61.0] | 171.9 [171.8–174.6] | 178.0 [175.5–179.3] | 29.0 [23.6–30.5] | 14.0 [13.0–15.0] |
| Meteor | v24.15.0 | hello | 100 | 3/3 | 123.7 [123.5–124.7] | 130.2 [128.1–131.3] | 159.1 [151.6–160.9] | 3.5 [3.5–3.6] | 15.0 [14.0–19.0] |
| Meteor | v24.15.0 | catalog | 100 | 3/3 | 124.7 [123.3–124.8] | 138.9 [138.8–139.4] | 162.5 [157.0–162.7] | 3.9 [3.9–4.0] | 15.0 [14.0–16.0] |
| Meteor | v24.15.0 | validate | 100 | 3/3 | 124.4 [123.2–124.6] | 137.0 [134.6–138.8] | 163.4 [161.2–169.2] | 3.9 [3.8–3.9] | 18.0 [15.0–22.0] |
| Meteor | v24.15.0 | upstream | 100 | 3/3 | 126.5 [126.2–127.6] | 187.0 [181.6–188.3] | 188.9 [184.5–191.2] | 8.0 [7.7–8.1] | 25.0 [24.0–25.0] |
| Meteor | v24.15.0 | hello | 1000 | 3/3 | 124.3 [123.4–125.5] | 168.6 [167.6–171.1] | 169.0 [168.5–172.3] | 12.8 [12.7–12.8] | 10.0 [8.0–18.0] |
| Meteor | v24.15.0 | catalog | 1000 | 3/3 | 124.1 [123.3–124.2] | 167.6 [167.6–172.5] | 168.0 [167.7–173.2] | 15.2 [15.1–15.9] | 10.0 [9.0–10.0] |
| Meteor | v24.15.0 | validate | 1000 | 3/3 | 125.2 [124.6–126.1] | 174.9 [173.8–175.0] | 176.3 [175.0–176.6] | 14.8 [14.6–14.9] | 10.0 [9.0–13.0] |
| Meteor | v24.15.0 | upstream | 1000 | 3/3 | 127.8 [127.2–128.3] | 213.9 [213.8–217.2] | 124.5 [122.1–126.7] | 38.2 [36.8–41.4] | 18.0 [17.0–19.0] |
| AdonisJS | v24.15.0 | hello | 100 | 3/3 | 114.9 [113.9–115.3] | 143.4 [143.0–149.2] | 144.3 [143.1–149.4] | 2.5 [2.3–2.5] | 9.0 [9.0–10.0] |
| AdonisJS | v24.15.0 | catalog | 100 | 3/3 | 114.4 [113.7–116.0] | 142.7 [142.7–142.9] | 143.6 [142.9–144.3] | 3.0 [3.0–3.1] | 13.0 [12.0–13.0] |
| AdonisJS | v24.15.0 | validate | 100 | 3/3 | 114.5 [114.3–115.0] | 147.0 [146.6–147.4] | 148.5 [148.1–148.5] | 3.0 [2.9–3.0] | 12.0 [12.0–13.0] |
| AdonisJS | v24.15.0 | upstream | 100 | 3/3 | 115.9 [112.8–116.2] | 195.2 [194.7–198.7] | 197.2 [194.8–199.7] | 6.3 [6.1–6.3] | 20.0 [20.0–21.0] |
| AdonisJS | v24.15.0 | hello | 1000 | 3/3 | 114.3 [113.6–115.4] | 145.3 [144.6–149.7] | 145.3 [144.6–149.6] | 10.7 [10.4–10.7] | 6.0 [6.0–7.0] |
| AdonisJS | v24.15.0 | catalog | 1000 | 3/3 | 113.7 [113.2–114.4] | 149.3 [149.2–151.7] | 151.8 [151.7–153.0] | 14.5 [14.4–14.7] | 9.0 [8.0–9.0] |
| AdonisJS | v24.15.0 | validate | 1000 | 3/3 | 115.2 [115.1–115.7] | 149.4 [149.0–149.8] | 149.4 [148.9–149.5] | 13.7 [13.6–14.3] | 9.0 [7.0–10.0] |
| AdonisJS | v24.15.0 | upstream | 1000 | 3/3 | 115.0 [114.8–115.5] | 208.4 [203.9–221.8] | 212.1 [206.0–223.5] | 37.8 [35.6–38.7] | 15.0 [15.0–17.0] |
| NestJS · Express | v24.15.0 | hello | 100 | 3/3 | 99.8 [99.7–100.9] | 126.2 [125.2–130.1] | 126.4 [125.2–130.1] | 2.1 [2.1–2.2] | 7.0 [7.0–8.0] |
| NestJS · Express | v24.15.0 | catalog | 100 | 3/3 | 100.3 [99.8–100.4] | 127.1 [127.0–127.7] | 127.3 [127.0–127.8] | 2.5 [2.5–2.6] | 10.0 [10.0–10.0] |
| NestJS · Express | v24.15.0 | validate | 100 | 3/3 | 100.4 [100.3–101.0] | 130.0 [129.3–130.4] | 130.5 [130.5–130.9] | 2.4 [2.3–2.4] | 9.0 [8.0–13.0] |
| NestJS · Express | v24.15.0 | upstream | 100 | 3/3 | 102.4 [101.9–102.9] | 188.1 [183.4–189.5] | 190.1 [185.1–192.4] | 6.2 [6.1–6.2] | 20.0 [20.0–22.0] |
| NestJS · Express | v24.15.0 | hello | 1000 | 3/3 | 100.4 [99.3–101.3] | 129.6 [129.4–130.0] | 129.8 [129.4–130.2] | 8.5 [8.5–8.6] | 5.0 [5.0–13.0] |
| NestJS · Express | v24.15.0 | catalog | 1000 | 3/3 | 100.8 [100.3–101.1] | 129.2 [127.9–132.9] | 129.4 [129.2–132.9] | 11.4 [11.3–11.5] | 11.0 [8.0–11.0] |
| NestJS · Express | v24.15.0 | validate | 1000 | 3/3 | 101.3 [101.2–101.4] | 132.2 [131.7–132.7] | 132.2 [131.7–132.7] | 10.7 [10.7–11.0] | 7.0 [6.0–8.0] |
| NestJS · Express | v24.15.0 | upstream | 1000 | 3/3 | 103.0 [101.1–103.4] | 201.2 [201.0–201.7] | 201.2 [201.1–201.7] | 37.1 [31.8–38.3] | 16.0 [16.0–17.0] |
| Total.js 5 | v24.15.0 | hello | 100 | 3/3 | 63.1 [63.0–63.2] | 94.6 [93.9–95.2] | 103.2 [103.1–104.0] | 1.6 [1.5–1.6] | 5.0 [5.0–6.0] |
| Total.js 5 | v24.15.0 | catalog | 100 | 3/3 | 63.3 [63.3–63.5] | 99.9 [99.9–100.7] | 107.5 [107.2–108.8] | 2.0 [1.9–2.1] | 7.0 [6.0–8.0] |
| Total.js 5 | v24.15.0 | validate | 100 | 3/3 | 63.2 [63.2–63.2] | 93.4 [92.2–94.2] | 96.7 [94.7–101.4] | 1.9 [1.8–2.0] | 7.0 [6.0–7.0] |
| Total.js 5 | v24.15.0 | upstream | 100 | 3/3 | 69.5 [69.4–69.5] | 168.3 [166.0–169.2] | 169.8 [167.4–172.5] | 5.6 [5.4–5.6] | 18.0 [18.0–18.0] |
| Total.js 5 | v24.15.0 | hello | 1000 | 3/3 | 62.8 [62.8–63.2] | 165.7 [164.9–166.7] | 173.8 [173.1–174.3] | 8.2 [8.1–8.2] | 6.0 [6.0–6.0] |
| Total.js 5 | v24.15.0 | catalog | 1000 | 3/3 | 63.3 [63.3–63.5] | 175.0 [173.3–175.6] | 175.5 [173.7–181.0] | 9.9 [9.9–9.9] | 6.0 [5.0–9.0] |
| Total.js 5 | v24.15.0 | validate | 1000 | 3/3 | 63.2 [63.0–63.5] | 169.2 [164.2–169.4] | 171.4 [170.9–171.4] | 9.1 [9.0–9.2] | 22.0 [6.0–27.0] |
| Total.js 5 | v24.15.0 | upstream | 1000 | 3/3 | 68.4 [68.1–69.2] | 205.0 [203.3–206.0] | 207.9 [203.8–210.7] | 36.4 [30.2–36.8] | 23.0 [22.0–24.0] |

[Mesures brutes](/benchmarks/linux-20261017/general.json)

[Sources figées et verrous de dépendances](/benchmarks/linux-20261017/sources.tar.gz)

[Rapport enregistré](/benchmarks/linux-20261017/general-report.md)

[CSV par scénario](/benchmarks/linux-20261017/general.csv)

## Reproduire et vérifier

Utilisez un répertoire vide avec Docker (conteneurs Linux), curl et tar. Node.js n’est pas nécessaire sur l’hôte : la vérification de la somme de contrôle et la suite de tests utilisent Node 24.15.0 dans Docker. La première construction télécharge des dépendances et compile Meteor, ce qui peut prendre plusieurs minutes. La préparation nécessite internet ; les conteneurs de test n’ont pas de réseau externe. Chaque scénario mesuré dure environ six minutes.

Le kit complet exécute les huit adaptateurs sur Node 24.15.0 dans des conteneurs Linux. Il comprend l’exécuteur, les adaptateurs, les verrous de dépendances et les instructions de compilation de Meteor. Les mesures publiées conservent leurs versions de runtime d’origine et l’archive de sources d’origine, disponibles dans les preuves ; ce kit n’uniformise que les nouvelles exécutions.

[Télécharger le kit de test complet](https://forge.totaljs.es/benchmarks/node-24.15.0/sources.tar.gz)

1. Télécharger et préparer. Le lien contient l’exécuteur, les adaptateurs et les verrous de dépendances ; aucune installation de Forge ni clonage de dépôt n’est nécessaire. Dans un répertoire vide, exécutez les commandes suivantes. Si vous avez téléchargé avec le bouton, gardez le nom sources.tar.gz et sautez curl.

```bash
curl --fail --location --output sources.tar.gz 'https://forge.totaljs.es/benchmarks/node-24.15.0/sources.tar.gz' &&
docker run --rm --platform linux/amd64 --network none -v "$PWD:/kit:ro" node:24.15.0-bookworm node -e 'const fs=require("node:fs"),crypto=require("node:crypto");if(crypto.createHash("sha256").update(fs.readFileSync("/kit/sources.tar.gz")).digest("hex")!=="65801cd865a3fc961e69ba8df421ed198acdc399d29934714fb954e4de1db651")throw Error("SHA-256 mismatch");' &&
tar -xzf sources.tar.gz &&
docker build --platform linux/amd64 -t forge-benchmark-local . &&
mkdir -p results
```

2. Vérifier l’installation (environ 15 à 30 secondes). Ce court test produit smoke.json et vérifie ses entrées enregistrées ; ce n’est pas un résultat de performance publiable.

```bash
docker run --rm --platform linux/amd64 --cpus 2 --memory 2g --memory-swap 2g --pids-limit 256 --read-only --network none --user "$(id -u):$(id -g)" --tmpfs /tmp:rw,nosuid,nodev,size=512m,mode=1777 -v "$PWD/results:/results" forge-benchmark-local node benchmarks/v2/run.cjs --smoke --frameworks totaljs --workloads hello --rates 1000 --out /results/smoke.json
docker run --rm --platform linux/amd64 --cpus 2 --memory 2g --memory-swap 2g --pids-limit 256 --read-only --network none --user "$(id -u):$(id -g)" --tmpfs /tmp:rw,nosuid,nodev,size=512m,mode=1777 -v "$PWD/results:/results" forge-benchmark-local node benchmarks/v2/verify.cjs /results/smoke.json --allow-smoke
```

3. Exécuter et vérifier la mesure choisie. Les résultats sont enregistrés dans results/my-run.json, dans le répertoire benchmarks choisi. L’exécuteur refuse d’écraser une sortie existante : utilisez un autre nom de fichier pour une répétition. La vérification contrôle l’exhaustivité et les entrées, pas l’égalité avec les chiffres publiés.

```bash
docker run --rm --platform linux/amd64 --cpus 2 --memory 2g --memory-swap 2g --pids-limit 256 --read-only --network none --user "$(id -u):$(id -g)" --tmpfs /tmp:rw,nosuid,nodev,size=512m,mode=1777 -v "$PWD/results:/results" forge-benchmark-local node benchmarks/v2/run.cjs --frameworks totaljs --workloads hello --rates 1000 --seconds 60 --warmup 15 --rest 30 --reps 3 --connections 32 --out /results/my-run.json
docker run --rm --platform linux/amd64 --cpus 2 --memory 2g --memory-swap 2g --pids-limit 256 --read-only --network none --user "$(id -u):$(id -g)" --tmpfs /tmp:rw,nosuid,nodev,size=512m,mode=1777 -v "$PWD/results:/results" forge-benchmark-local node benchmarks/v2/verify.cjs /results/my-run.json
```

[JSON](/benchmarks/summary.json) · [Revue d’intégrité et d’environnement](/benchmarks/linux-20261017/review.json) · [SHA-256](/benchmarks/linux-20261017/SHA256SUMS.txt)
