perf(runtime): stop rebuilding per-route data on every request - #1875
Open
cesco69 wants to merge 1 commit into
Open
perf(runtime): stop rebuilding per-route data on every request#1875cesco69 wants to merge 1 commit into
cesco69 wants to merge 1 commit into
Conversation
getValidatedArgs did two things on every request that do not depend on the request: - Object.values(args) rebuilt the parameter list, although the generated routes create one args object per route at registration time and pass back the very same object every time. It is now derived once and cached in a WeakMap keyed by that object. - Object.keys(fieldErrors).length > 0 allocated an array of keys only to find out whether the object was empty. Replaced with an allocation-free check. Both helpers live on TemplateService so express, koa and hapi share them. No behavioural change: same values, same ValidateError, same field errors.
iffa
added a commit
to iffa/tsoa
that referenced
this pull request
Aug 24, 2026
The generated routes build one args object per route at registration and hand back that same object on every request, so Object.values(args) was rebuilt per request for a list that never changes. Measured on a four-parameter route: 956 -> 932 ns per getValidatedArgs call, best of five. Upstream lukeautry#1875 reports 45%, which does not reproduce; its second change, a for..in over fieldErrors, is worth about 1ns and is left out.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
perf(runtime): stop rebuilding per-route data on every request
What
getValidatedArgsdoes two things on every request that do not depend on the request:Object.values(args)rebuilds the parameter list. But the generated routes buildone
argsobject per route at registration time and hand back that very same object onevery request, so the derived list is stable. It is now computed once and cached in a
WeakMapkeyed by that object.Object.keys(fieldErrors).length > 0allocates an array of keys only to find outwhether the object is empty. Replaced with an allocation-free
for...incheck.Both helpers live on
TemplateService, so the express, koa and hapi services share them.The
formDatabranch, which callsObject.values(args).filter(...), uses the cached listtoo.
The change is deliberately small:
.map()and theswitchare untouched.Measurements
Node 26, best of 5 rounds, 300k iterations per measurement. One route with four parameters
(
{in:'path',dataType:'double'},{in:'request'},{in:'query',dataType:'string'},{in:'query',dataType:'double'}):45% faster, on a route of typical shape.
Measured with the
validatorfix fromvalidatorjs/validator.js applied, because
that one dominates otherwise:
isFloatcompiles aRegExpper call andtoFloatcallsisFloatagain, which alone accounts for 85% ofgetValidatedArgs. Without that fix thenumbers here are the same in absolute terms but a much smaller share of the total.
Correctness
ValidateError, samefieldErrorscontents.WeakMapis keyed by theargsobject identity, so routes never share a list, andentries are collected with the route.
argsobject per request (a hand-written template, forinstance) the cache simply never hits: behaviour is unchanged, cost is one failed
WeakMaplookup.A caveat on verification
I could not run tsoa's own test suite in my environment:
yarn installcannot reach theregistry from here, and
npm installinpackages/runtimetriggers thepreparescript,which needs lerna. Please run
yarn testbefore merging. The change is mechanical andthe semantics are argued above, but I would not want that to stand in for the suite.
Possible follow-up
Once these allocations are gone, the remaining per-request cost is the
switch (param.in)plus
ValidateParam'sswitch (property.dataType)— string dispatch over metadata that isconstant per parameter. Building one closure per parameter at registration time, instead of
interpreting the schema on each request, would be the structural fix. That is a much larger
change and I have not attempted it here.