From 65a94a6176e3a76ca52d0666cf882689ac0b5b9c Mon Sep 17 00:00:00 2001 From: Marcos Casagrande Date: Fri, 22 Sep 2023 04:06:42 +0200 Subject: [PATCH] perf(ext/fetch): use new instead of createBranded (#20624) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR optimizes `fromInner*` methods of `Request` / `Header` / `Response` used by `Deno.serve` and `fetch` by using `new` instead of `ObjectCreate` from `createBranded`. The "brand" is created by passing `webidl.brand` to the constructor instead. https://github.com/denoland/deno/blob/142449ecab20006c5cfd15462814650596bc034d/ext/webidl/00_webidl.js#L1001-L1005 ### Benchmark ```js const createBranded = Symbol("create branded"); const brand = Symbol("brand"); class B { constructor(init) { if (init === createBranded) { this[brand] = brand; } } } Deno.bench("Object.create(protoype)", () => { Object.create(B.prototype); }); Deno.bench("new Class", () => { new B(createBranded); }); ``` ``` cpu: 13th Gen Intel(R) Core(TM) i9-13900H runtime: deno 1.37.0 (x86_64-unknown-linux-gnu) benchmark time (avg) iter/s (min … max) p75 p99 p995 ----------------------------------------------------------------------------- ----------------------------- Object.create(protoype) 8.74 ns/iter 114,363,610.3 (7.32 ns … 26.02 ns) 8.65 ns 13.39 ns 14.47 ns new Class 3.05 ns/iter 328,271,012.2 (2.78 ns … 9.1 ns) 3.06 ns 3.46 ns 3.5 ns ``` --- ext/fetch/20_headers.js | 10 ++++++++-- ext/fetch/23_request.js | 10 ++++++++-- ext/fetch/23_response.js | 10 ++++++++-- 3 files changed, 24 insertions(+), 6 deletions(-) diff --git a/ext/fetch/20_headers.js b/ext/fetch/20_headers.js index 39127b1ecf2e2b..a004daa897465b 100644 --- a/ext/fetch/20_headers.js +++ b/ext/fetch/20_headers.js @@ -40,6 +40,7 @@ const _headerList = Symbol("header list"); const _iterableHeaders = Symbol("iterable headers"); const _iterableHeadersCache = Symbol("iterable headers cache"); const _guard = Symbol("guard"); +const _brand = webidl.brand; /** * @typedef Header @@ -286,12 +287,17 @@ class Headers { /** @param {HeadersInit} [init] */ constructor(init = undefined) { + if (init === _brand) { + this[_brand] = _brand; + return; + } + const prefix = "Failed to construct 'Headers'"; if (init !== undefined) { init = webidl.converters["HeadersInit"](init, prefix, "Argument 1"); } - this[webidl.brand] = webidl.brand; + this[_brand] = _brand; this[_guard] = "none"; if (init !== undefined) { fillHeaders(this, init); @@ -486,7 +492,7 @@ webidl.converters["Headers"] = webidl.createInterfaceConverter( * @returns {Headers} */ function headersFromHeaderList(list, guard) { - const headers = webidl.createBranded(Headers); + const headers = new Headers(_brand); headers[_headerList] = list; headers[_guard] = guard; return headers; diff --git a/ext/fetch/23_request.js b/ext/fetch/23_request.js index 5232cc13c96c97..c09bd4880877ab 100644 --- a/ext/fetch/23_request.js +++ b/ext/fetch/23_request.js @@ -52,6 +52,7 @@ const _mimeType = Symbol("mime type"); const _body = Symbol("body"); const _url = Symbol("url"); const _method = Symbol("method"); +const _brand = webidl.brand; /** * @param {(() => string)[]} urlList @@ -275,6 +276,11 @@ class Request { * @param {RequestInit} init */ constructor(input, init = {}) { + if (input === _brand) { + this[_brand] = _brand; + return; + } + const prefix = "Failed to construct 'Request'"; webidl.requiredArguments(arguments.length, 1, prefix); input = webidl.converters["RequestInfo_DOMString"]( @@ -284,7 +290,7 @@ class Request { ); init = webidl.converters["RequestInit"](init, prefix, "Argument 2"); - this[webidl.brand] = webidl.brand; + this[_brand] = _brand; /** @type {InnerRequest} */ let request; @@ -554,7 +560,7 @@ function toInnerRequest(request) { * @returns {Request} */ function fromInnerRequest(inner, signal, guard) { - const request = webidl.createBranded(Request); + const request = new Request(_brand); request[_request] = inner; request[_signal] = signal; request[_getHeaders] = () => headersFromHeaderList(inner.headerList, guard); diff --git a/ext/fetch/23_response.js b/ext/fetch/23_response.js index dc4e7543420af5..73a90166d590ac 100644 --- a/ext/fetch/23_response.js +++ b/ext/fetch/23_response.js @@ -60,6 +60,7 @@ const _response = Symbol("response"); const _headers = Symbol("headers"); const _mimeType = Symbol("mime type"); const _body = Symbol("body"); +const _brand = webidl.brand; /** * @typedef InnerResponse @@ -305,6 +306,11 @@ class Response { * @param {ResponseInit} init */ constructor(body = null, init = undefined) { + if (body === _brand) { + this[_brand] = _brand; + return; + } + const prefix = "Failed to construct 'Response'"; body = webidl.converters["BodyInit_DOMString?"](body, prefix, "Argument 1"); init = webidl.converters["ResponseInit_fast"](init, prefix, "Argument 2"); @@ -320,7 +326,7 @@ class Response { bodyWithType = extractBody(body); } initializeAResponse(this, init, bodyWithType); - this[webidl.brand] = webidl.brand; + this[_brand] = _brand; } /** @@ -489,7 +495,7 @@ function toInnerResponse(response) { * @returns {Response} */ function fromInnerResponse(inner, guard) { - const response = webidl.createBranded(Response); + const response = new Response(_brand); response[_response] = inner; response[_headers] = headersFromHeaderList(inner.headerList, guard); return response;