Skip to content

perf(ext/web): reduce tee() per-chunk overhead #35771

Description

@bartlomieju

tee() + draining both branches sustains ~2.18M source chunks/sec vs ~6.7M for a raw reader.read() loop (64 KiB chunks, 32 MiB per pass, macOS arm64, canary, steady state after JIT warmup) — ~460ns per source chunk vs ~150ns.

readableStreamDefaultTee's pull-through allocates a read request per chunk, enqueues into both branch controllers (each paying the queue-wrapper and pull-if-needed promise costs), and re-arms via microtask (readAgain handling) per chunk.

Worth investigating: synchronous re-arm when both branches have outstanding demand, cutting the per-chunk deferred, and letting the branch controllers share the chunk without redundant bookkeeping. This overlaps with the queue-wrapper and read-path allocation issues, so it should land after/with those and be re-measured.

Repro benchmark (same harness as the linked read-path issue, scenario):

const [a, b] = makeStream().tee();
const drain = async (s) => {
  const r = s.getReader();
  while (!(await r.read()).done);
};
await Promise.all([drain(a), drain(b)]);

Related: #35768 (read/pipe path allocations), #35769 (identity pipeThrough)

Metadata

Metadata

Assignees

Labels

ext/webrelated to the ext/web crateperfperformance relatedwebrelated to Web APIs

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions