1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2025-01-03 04:48:52 -05:00

perf(ext/headers): optimize headers iterable (#20155)

This PR makes more optimizations to headers iterable by removing
`ObjectEntries` which was consistently prominent in the flame graph when
benchmarking an express server.

**this PR**

```
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 iter        9.6 µs/iter     104,134.1   (8.74 µs … 131.31 µs)   9.47 µs  12.61 µs  17.81 µs
```

**main**
```
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 iter      12.87 µs/iter      77,675.9  (11.97 µs … 132.34 µs)  12.76 µs  16.49 µs   26.4 µs
```


```js
const headers = new Headers({
  "Content-Type": "application/json",
  "X-Content-Type": "application/json",
  "Date": "Thu, 14 Aug 2023 17: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 iter', () => {
  [...headers]
})
```
This commit is contained in:
Marcos Casagrande 2023-08-14 21:13:55 +02:00 committed by GitHub
parent e071382768
commit 625bd39050
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -26,7 +26,6 @@ const {
ArrayPrototypeSort, ArrayPrototypeSort,
ArrayPrototypeJoin, ArrayPrototypeJoin,
ArrayPrototypeSplice, ArrayPrototypeSplice,
ObjectEntries,
ObjectHasOwn, ObjectHasOwn,
RegExpPrototypeTest, RegExpPrototypeTest,
Symbol, Symbol,
@ -238,8 +237,8 @@ class Headers {
// The order of steps are not similar to the ones suggested by the // The order of steps are not similar to the ones suggested by the
// spec but produce the same result. // spec but produce the same result.
const headers = {}; const seenHeaders = {};
const cookies = []; const entries = [];
for (let i = 0; i < list.length; ++i) { for (let i = 0; i < list.length; ++i) {
const entry = list[i]; const entry = list[i];
const name = byteLowerCase(entry[0]); const name = byteLowerCase(entry[0]);
@ -250,25 +249,23 @@ class Headers {
// so must be given to the user as multiple headers. // so must be given to the user as multiple headers.
// The else block of the if statement is spec compliant again. // The else block of the if statement is spec compliant again.
if (name === "set-cookie") { if (name === "set-cookie") {
ArrayPrototypePush(cookies, [name, value]); ArrayPrototypePush(entries, [name, value]);
} else { } else {
// The following code has the same behaviour as getHeader() // The following code has the same behaviour as getHeader()
// at the end of loop. But it avoids looping through the entire // at the end of loop. But it avoids looping through the entire
// list to combine multiple values with same header name. It // list to combine multiple values with same header name. It
// instead gradually combines them as they are found. // instead gradually combines them as they are found.
let header = headers[name]; const seenHeaderIndex = seenHeaders[name];
if (header && header.length > 0) { if (seenHeaderIndex !== undefined) {
header += "\x2C\x20" + value; const entryValue = entries[seenHeaderIndex][1];
entries[seenHeaderIndex][1] = entryValue.length > 0
? entryValue + "\x2C\x20" + value
: value;
} else { } else {
header = value; seenHeaders[name] = entries.length; // store header index in entries array
} ArrayPrototypePush(entries, [name, value]);
headers[name] = header;
} }
} }
const entries = ObjectEntries(headers);
for (let i = 0; i < cookies.length; ++i) {
ArrayPrototypePush(entries, cookies[i]);
} }
ArrayPrototypeSort( ArrayPrototypeSort(