Skip to content

perf(router): index exact static handlers - #31

Merged
Upd4ting merged 1 commit into
mainfrom
perf/static-route-dispatch
Aug 19, 2026
Merged

perf(router): index exact static handlers#31
Upd4ting merged 1 commit into
mainfrom
perf/static-route-dispatch

Conversation

@Upd4ting

@Upd4ting Upd4ting commented Aug 19, 2026

Copy link
Copy Markdown
Member

Résumé

  • ajoute un index exact compilé à l'enregistrement, par méthode et chemin statique
  • évite sur un hit statique unique les allocations du tableau résultat, de HandlerResult et des paramètres vides
  • conserve le trie pour dynamique/catch-all/any et multi-handlers
  • invalide l'entrée lors des ajouts/retraits et la restaure lorsqu'un endpoint redevient mono-handler

L'index contient uniquement des chemins enregistrés, jamais les URL reçues. Il est activé pour les handlers HTTP et WebSocket mono-handler ; prefix/postfix/monitor restent sur le pipeline existant.

Rebase #29 + #30

Head f3bf6bf6d064965885d9c582898574995230b7b8, rebasé sur f97225872255552f35fe148a3f0b618995631620.

La résolution conserve strictement :

Le retrait récursif retourne le nombre réellement supprimé : handlerCounts reste donc correct, y compris lorsque plusieurs handlers d'un même niveau partagent un ID, et l'index exact est invalidé au même endroit.

Compatibilité vérifiée

  • précédence statique > dynamique > catch-all et méthode > any
  • fallback HEAD vers GET et OPTIONS
  • priorités et fast paths du pipeline multi-handler
  • ajout, retrait et hot reload
  • handlers dupliqués sur une route et IDs dupliqués entre routes
  • suppression des Maps vides : aucune croissance liée au trafic

Benchmarks rebased

Baseline : f972258 (#29 + #30). Candidat : f3bf6bf (#29 + #30 + #31).

Orb x64, Node 20.9.0, autocannon 8, loopback, réponse "ok", pipelining=1. Chaque chiffre est la moyenne de 5 répétitions alternées baseline/candidat, après chauffe de 1 s, fenêtres de 2 s. p99 vaut 0 ms à la résolution milliseconde d'autocannon ; 0 erreur partout.

Pile HTTP complète

scénario baseline req/s candidat req/s delta CV base / candidat
statique simple, c=10 38 794 40 250 +3,75 % 4,02 / 0,76 %
statique simple, c=100 39 763 40 646 +2,22 % 1,88 / 3,33 %
dernière / 1 000, c=10 37 658 38 803 +3,04 % 3,71 / 4,62 %
dernière / 1 000, c=100 38 794 39 939 +2,95 % 1,34 / 1,14 %
dernière / 10 000, c=10 40 349 41 130 +1,94 % 1,33 / 1,52 %
dernière / 10 000, c=100 38 675 38 336 -0,88 % 3,15 / 3,37 %
dynamique contrôle, c=100 36 480 34 790 -4,63 % 2,93 / 4,15 %
catch-all contrôle, c=100 35 952 36 038 +0,24 % 3,80 / 5,65 %
pipeline contrôle, c=100 39 370 38 390 -2,49 % 2,97 / 4,24 %

L'objectif 40k est atteint sur la route simple et sur la dernière route / 10 000 à c=10. La dernière route / 1 000 atteint 39,94k à c=100, soit 61 req/s sous le seuil. Le -0,88 % statique / 10 000 c=100 et le -2,49 % pipeline sont inférieurs ou proches de la variance observée. Le contrôle dynamique mérite davantage d'attention : le miss exact ajoute nécessairement un lookup Map avant le trie.

Micro-routeur

scénario baseline ops/s candidat ops/s delta CV base / candidat
statique simple 9,93 M 35,35 M +256,1 % 1,08 / 4,16 %
dernière / 1 000 9,90 M 27,33 M +176,2 % 1,08 / 4,96 %
dernière / 10 000 7,35 M 26,28 M +257,7 % 3,90 / 6,32 %
dynamique contrôle / 1 000 3,39 M 3,17 M -6,63 % 1,28 / 0,70 %
catch-all contrôle / 1 000 2,27 M 2,17 M -4,22 % 1,86 / 0,77 %

La régression micro des misses est réelle et constitue la limite principale de cette approche ; les algorithmes du trie dynamique/catch-all eux-mêmes restent inchangés.

Mémoire

Après GC forcé et en moyennant les processus c=10/c=100, surcoût de heap d'enregistrement relatif à la baseline : environ +0,19 MiB à 1 000 routes et +1,64 MiB à 10 000 routes (~172 octets/entrée à 10k). L'index référence directement le callback existant ; le coût vient de la clé normalisée, de la Map et de la métadonnée d'invalidation.

Comparaison indicative, même fixture statique 1 000

framework c=10 req/s c=100 req/s erreurs
Antelope candidat, dernière route 38 803 39 939 0
Fastify 5.5 45 859 49 459 0
Nest 11.1 + adaptateur Fastify 38 349 39 984 0

Même orb, chemin, réponse et protocole. Nest/Fastify n'exécutent pas le pipeline Antelope. Adonis reste exclu plutôt que mesuré via un faux standalone : son bootstrap applicatif et ses providers sont nécessaires à une comparaison honnête.

Validation

  • pnpm install --frozen-lockfile
  • pnpm build
  • pnpm dlx @antelopejs/core module test . — 132 tests passants
  • pnpm lint
  • diff incrémental origin/main...HEAD inspecté ; seuls src/server.ts et les tests perf(router): index exact static handlers #31 changent

Copy link
Copy Markdown
Member Author

Benchmark d’intégration — toutes les optimisations HTTP

J’ai assemblé localement, sans pousser de branche d’intégration, les têtes de api#29 à api#35 avec interface-api#14 (7602636). Build OK et 174 tests passants avec l’interface optimisée.

Méthode : même orb et même suite que le baseline initial, Node 24.19, serveur CPU 2, autocannon CPU 4/6, 3 répétitions, chauffe 2 s + mesure 5 s, ordre randomisé. Tous les runs retenus ont 0 erreur, 0 timeout et 0 non-2xx.

Scénario Baseline Antelope Toutes optis Gain p99 avant → après
GET JSON c=10 22 062 32 170 +45,8 % 0 → 0 ms
GET JSON c=100 22 696 31 771 +40,0 % 7 → 6 ms
Pipeline 10 31 186 34 504 +10,6 % 48 → 42 ms
Route dynamique 22 085 30 616 +38,6 % 8 → 5 ms
POST JSON 15 173 19 768 +30,3 % 9 → 8 ms
1 000 routes 22 434 30 664 +36,7 % 6 → 5 ms

Comparaison dans le même run final :

Scénario Antelope Adonis 7.4 Nest/Fastify Position Antelope
GET c=10 32 170 32 304 35 728 ≈ Adonis (−0,4 %), −10,0 % Fastify
GET c=100 31 771 31 507 36 061 +0,8 % Adonis, −11,9 % Fastify
Pipeline 10 34 504 37 866 44 880 −8,9 % Adonis, −23,1 % Fastify
Dynamique 30 616 31 451 33 133 −2,7 % Adonis, −7,6 % Fastify
POST JSON 19 768 11 350 12 431 +74,2 % / +59,0 %
1 000 routes 30 664 10 799 36 534 +183,9 % Adonis, −16,1 % Fastify

Conclusion honnête : le cumul apporte un gros saut et dépasse Adonis dans plusieurs cas, mais ne dépasse pas encore Nest/Fastify hors POST. Par rapport à l’intégration précédente #29 + #30, les cinq nouvelles PR apportent +4,7 à +7,5 % sur les GET usuels/dynamiques/1 000 routes, mais le pipeline recule de 6,3 % ; c’est le prochain point critique à profiler. Le cold start reste à 1 133 ms car cette campagne utilise encore le CLI standard ; le launcher de production est traité séparément dans AntelopeJS/antelopejs#96.

@Upd4ting
Upd4ting force-pushed the perf/static-route-dispatch branch from 4da22ef to f3bf6bf Compare August 19, 2026 18:07
@Upd4ting
Upd4ting merged commit 04f8294 into main Aug 19, 2026
2 checks passed
@Upd4ting
Upd4ting deleted the perf/static-route-dispatch branch August 19, 2026 19:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants