perf: memoise the Content-Type resolution in res.set, res.type and setCharset - #7481
Open
nigrosimone wants to merge 1 commit into
Open
nigrosimone wants to merge 1 commit into
nigrosimone wants to merge 1 commit into
Conversation
nigrosimone
marked this pull request as ready for review
September 20, 2026 06:17
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.
A response sets one of a handful of content types, and each of them goes through the mime database (
mime.contentTypeinres.setandres.type) and, for a string body, through content-type's parse and format (setCharsetinres.send). Profiled under load with autocannon, that is about 1.9 us perres.jsonand 0.9 us perres.sendof a string, the largest cost in Express's own code after the prototype switch inapp.handle.This memoises both by their input, in maps capped at 100 entries. Same output for the same input, nothing observable changes.
setCharseton the json type goes from 470 ns to 11. The cache forsetCharsetis a map per charset: a key made by joining the two strings measured 240 ns, since the joined string is hashed again on every call.Self time per request, node 26, before and after:
res.jsonroute, mime.contentType 400 ns, content-type parse 280-315, parseParameters 255-303, format 218-291, skipValue 270, setCharset 233, charset 218, after only setCharset at 11 ns;res.send("Hello World"), mime.contentType 264-330, charset 190-207, lookup 154-159, res.type 314-384, after res.type at 132-178.This is the same memoisation I did in fulmine.js, an Express 5 replacement on uWebSockets.js that keeps the API, where
mime.contentTypemeasured 273 ns a response and 6 memoised. It applies here as it is, sinceres.setandres.sendsee the same handful of types.