From d0525dd692e7771d5742202db623e0f4a4fe750c Mon Sep 17 00:00:00 2001 From: Marcos Casagrande Date: Sat, 12 Aug 2023 18:42:23 +0200 Subject: [PATCH] perf(ext/headers): use regex.test instead of .exec (#20125) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR improves the performance of `Headers.get` by using `Regex.test` instead of `.exec`. Also replaced the `Map` used for caching with an object which is a bit faster **This patch** ``` cpu: 13th Gen Intel(R) Core(TM) i9-13900H runtime: deno 1.36.1 (x86_64-unknown-linux-gnu) benchmark time (avg) iter/s (min … max) p75 p99 p995 ----------------------------------------------------------------------- ----------------------------- Headers.get 124.71 ns/iter 8,018,687.3 (115.11 ns … 265.66 ns) 126.05 ns 136.12 ns 142.37 ns ``` **1.36.1** ``` cpu: 13th Gen Intel(R) Core(TM) i9-13900H runtime: deno 1.36.0 (x86_64-unknown-linux-gnu) benchmark time (avg) iter/s (min … max) p75 p99 p995 ----------------------------------------------------------------------- ----------------------------- Headers.get 218.91 ns/iter 4,568,172.3 (165.37 ns … 264.44 ns) 241.62 ns 260.94 ns 262.67 ns ``` ```js const headers = new Headers({ "Content-Type": "application/json", "Date": "Thu, 10 Aug 2023 07:45:10 GMT", "X-Deno": "Deno", "Powered-By": "Deno", "Content-Encoding": "gzip", "Set-Cookie": "__Secure-ID=123; Secure; Domain=example.com", "Content-Length": "150", "Vary": "Accept-Encoding, Accept, X-Requested-With", }); Deno.bench("Headers.get", () => { headers.get("x-deno"); }); ``` --- ext/fetch/20_headers.js | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/ext/fetch/20_headers.js b/ext/fetch/20_headers.js index 9e8f994fed..929eaf816a 100644 --- a/ext/fetch/20_headers.js +++ b/ext/fetch/20_headers.js @@ -28,12 +28,7 @@ const { ArrayPrototypeSplice, ObjectEntries, ObjectHasOwn, - RegExpPrototypeExec, - SafeMap, - MapPrototypeGet, - MapPrototypeHas, - MapPrototypeSet, - MapPrototypeClear, + RegExpPrototypeTest, Symbol, SymbolFor, SymbolIterator, @@ -102,19 +97,23 @@ function checkForInvalidValueChars(value) { return true; } -const HEADER_NAME_CACHE = new SafeMap(); +let HEADER_NAME_CACHE = {}; +let HEADER_CACHE_SIZE = 0; const HEADER_NAME_CACHE_SIZE_BOUNDARY = 4096; function checkHeaderNameForHttpTokenCodePoint(name) { - if (MapPrototypeHas(HEADER_NAME_CACHE, name)) { - return MapPrototypeGet(HEADER_NAME_CACHE, name); + const fromCache = HEADER_NAME_CACHE[name]; + if (fromCache !== undefined) { + return fromCache; } - const valid = RegExpPrototypeExec(HTTP_TOKEN_CODE_POINT_RE, name) !== null; + const valid = RegExpPrototypeTest(HTTP_TOKEN_CODE_POINT_RE, name); - if (HEADER_NAME_CACHE.size > HEADER_NAME_CACHE_SIZE_BOUNDARY) { - MapPrototypeClear(HEADER_NAME_CACHE); + if (HEADER_CACHE_SIZE > HEADER_NAME_CACHE_SIZE_BOUNDARY) { + HEADER_NAME_CACHE = {}; + HEADER_CACHE_SIZE = 0; } - MapPrototypeSet(HEADER_NAME_CACHE, name, valid); + HEADER_CACHE_SIZE++; + HEADER_NAME_CACHE[name] = valid; return valid; }