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