deno
ceec4e06 - perf(ext/web): fast path for TextEncoder.encodeInto (#33675)

Commit
87 days ago
perf(ext/web): fast path for TextEncoder.encodeInto (#33675) ## Summary Every call to `encoder.encodeInto(string, uint8array)` was going through two WebIDL converters and allocating a fresh `{ allowShared: true }` options object — the Uint8Array converter's opts is keyword-arg-style, so a fresh object literal is built on every call. For small inputs the converter chain plus that allocation is a ~45% tax on top of the actual op cost. This PR adds an inline guard at the top of `encodeInto`: if `source` is already a string and `destination` is already a `Uint8Array` (`@@toStringTag === "Uint8Array"`), skip both converters and call `op_encoding_encode_into` directly. The op handler already replaces lone surrogates with `U+FFFD`, matching `USVString` semantics — which is why the existing slow path was using `DOMString` rather than `USVString` and noting the spec deviation in the comment that this PR keeps. For the slow path (non-string `source`, non-`Uint8Array` `destination`, e.g. `encodeInto(123, dataView)`), behaviour is identical: the converters still run and throw / coerce as before. The shared `{ allowShared: true }` opts object is hoisted to module scope so even the slow path no longer allocates it on every call. ## Numbers `cargo bench -p deno_web --bench encoding`, release, mean of 5 runs: | bench | before | after | Δ | |---|---:|---:|---:| | `bench_encode_into_short` | 297 ns/iter | 161 ns/iter | **−45.8%** | `ns/iter` is the bench harness loop time for 1000 inner iterations, so per-call this is ~136 ns saved on a ~285 ns operation. The added bench encodes a 12-byte string into a 64-byte buffer with a reused `TextEncoder` — the shape every streaming encoder / incremental writer hits in a tight loop.
Author
Parents
Loading