Skip to content

perf(ext/web): optimize TextEncoder/TextDecoder hot paths#32735

Merged
bartlomieju merged 1 commit intomainfrom
perf/text-encoding
Mar 17, 2026
Merged

perf(ext/web): optimize TextEncoder/TextDecoder hot paths#32735
bartlomieju merged 1 commit intomainfrom
perf/text-encoding

Conversation

@bartlomieju
Copy link
Copy Markdown
Member

Summary

Optimizes the JS-side hot paths for TextEncoder.encode(), TextDecoder.decode(), and new TextDecoder() by skipping unnecessary webidl conversion overhead for the most common argument types.

Benchmark results (2M iterations, macOS ARM64)

Benchmark Before After Improvement
TextEncoder.encode() (13 chars) 8.1M ops/s 8.8M ops/s +9%
TextEncoder.encode() (85 chars) 7.5M ops/s 8.4M ops/s +13%
TextEncoder.encode() (1020 chars) 5.8M ops/s 6.3M ops/s +9%
TextDecoder.decode() (85 bytes) 21.0M ops/s 22.6M ops/s +8%
TextDecoder.decode() (49 bytes) 21.2M ops/s 23.6M ops/s +11%
new TextDecoder() 3.1M ops/s 4.0M ops/s +27%
new TextDecoder("utf-8") 3.1M ops/s 3.9M ops/s +25%

Changes

TextEncoder.encode(): Skip webidl.converters.DOMString when input is already a string (typeof check instead of function call + type dispatch). The converter ultimately returns the string unchanged for string inputs, so the check is redundant.

TextDecoder.decode(): Add fast path for Uint8Array input (the most common case). Checks TypedArrayPrototypeGetSymbolToStringTag directly instead of going through the full webidl.converters.BufferSource validation chain (ArrayBufferIsViewgetBufferisSharedArrayBuffer). Falls through to the full validation for other BufferSource types.

new TextDecoder() constructor: Short-circuit op_encoding_normalize_label for the three standard UTF-8 labels ("utf-8", "utf8", "unicode-1-1-utf-8"). This avoids crossing the JS→Rust boundary entirely for the most common encoding.

Test plan

  • Manual verification that encode/decode produce correct results
  • Existing unit tests pass
  • WPT encoding tests pass

🤖 Generated with Claude Code

- TextEncoder.encode(): skip webidl.converters.DOMString when input is
  already a string (typeof check instead of function call + type checks)
- TextDecoder.decode(): fast path for Uint8Array input, skip full
  BufferSource validation (avoids ArrayBufferIsView + getBuffer +
  isSharedArrayBuffer chain)
- TextDecoder constructor: fast path for common UTF-8 labels ("utf-8",
  "utf8", "unicode-1-1-utf-8") to avoid op_encoding_normalize_label
  Rust op call

Benchmark improvements:
- TextEncoder.encode(): +9-13%
- TextDecoder.decode(): +8-11% (medium/path sizes)
- new TextDecoder(): +25-27%

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Member

@littledivy littledivy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool

@bartlomieju bartlomieju merged commit 44301a6 into main Mar 17, 2026
112 checks passed
@bartlomieju bartlomieju deleted the perf/text-encoding branch March 17, 2026 14:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants