Repository navigation
perf(ext/web): synchronous transform fast path for TransformStream - #35844
Merged
Merged
Conversation
A JS transformer's transform() was invoked through webidl's `Promise<undefined>` return converter and its result chained with transformPromiseWith(), so every chunk through a non-identity TransformStream allocated two promises and took a microtask hop -- even when transform() completed synchronously. Only the identity transform had a fast path. Invoke transform() directly and return the synchronous-completion sentinel (undefined) on any non-thenable result, so transformStreamDefaultControllerPerformTransform resolves the write with a shared resolved promise and no microtask hop, mirroring the identity fast path and the default controller's synchronous pull. A synchronous throw becomes a rejected promise (the stream errors as before); a thenable return keeps the full async path. TextEncoderStream and TextDecoderStream transforms are made synchronous (return undefined instead of PromiseResolve()) so they take this path. Generic non-identity transform pipes improve ~6-11% (now faster than Bun); TextEncoderStream/TextDecoderStream ~6%.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
A JS transformer's
transform()was invoked through WebIDL'sPromise<undefined>return converter and its result chained with
transformPromiseWith(), so everychunk through a non-identity
TransformStreamallocated two promises and tooka microtask hop β even when
transform()completed synchronously. Only theidentity transform had a fast path.
This invokes
transform()directly and returns the synchronous-completionsentinel (
undefined) on any non-thenable result, sotransformStreamDefaultControllerPerformTransformresolves the write with ashared resolved promise and no microtask hop β mirroring the existing identity
fast path and the default controller's synchronous-pull fast path (#35843). A
synchronous throw becomes a rejected promise (the stream errors as before); a
thenable return keeps the full async path (matching
webidl.invokeCallbackFunction'sreturnsPromisebehavior).TextEncoderStreamandTextDecoderStreamtransforms are made synchronous(return
undefinedinstead ofPromiseResolve()) so they take this path.Benchmarks
macOS arm64, best-of-3, 100k chunks. Baseline is this branch with the change
reverted (identical build config).
TextEncoderStreamβTextDecoderStreampipeFor reference (Bun 1.4 canary): generic transform 2.14M/s, 3Γ chain 1.03M/s,
Text En/Decode 1.21M/s. This change moves generic transform pipes past Bun;
the remaining Text En/Decode gap is
core.encode/TextDecoder.decodeop cost(native in Bun), not the stream machinery.
Correctness / notes
resolves synchronously instead of a microtask later). Reuses the existing
resolvedPromise()used by the identity path.across chunks, lone high surrogate β replacement char, streaming multi-byte
UTF-8 split across byte chunks, fatal decode error propagation, input
coercion.
tests/unit/text_encoding_test.ts+tests/unit/streams_test.ts: 89/89 pass.streams+encodingcoverage.