Skip to content

url: create URLSearchParams collision map lazily - #65801

Open
RomainLanz wants to merge 1 commit into
nodejs:mainfrom
RomainForks:perf/urlsearchparams-lazy-map
Open

url: create URLSearchParams collision map lazily#65801
RomainLanz wants to merge 1 commit into
nodejs:mainfrom
RomainForks:perf/urlsearchparams-lazy-map

Conversation

@RomainLanz

Copy link
Copy Markdown
Contributor

Note

AI assistance was used to investigate this change and draft parts of the
implementation, tests, benchmarks, and PR description. I reviewed and
verified the final changes and benchmark results.

Hey! πŸ‘‹

new URLSearchParams(record) currently creates and updates a SafeMap for every record.

Most property names do not need this map. JavaScript object keys are already unique, so no collision is possible while toUSVString(key) leaves the key unchanged.

A map is only needed when a property name contains malformed UTF-16. toUSVString() replaces the invalid part with οΏ½ (U+FFFD), which can make two different property names identical:

'\uFFFDx'// remains '\uFFFDx''\uD835x'// becomes '\uFFFDx'

This change appends entries directly while keys remain unchanged. When the first key changes during conversion, it creates the SafeMap, adds the entries already processed, and continues with the existing collision handling. The later value still replaces the earlier one without changing its position.

A test covers a collision occurring after regular keys have already been processed.

Benchmark

Node v27.0.0-pre, V8 14.6.202.34-node.29, 15 runs with 4 million constructions per run:

node benchmark/compare.js \ --old ./node-before \ --new ./node-after \ --runs 15 \ --filter url-searchparams-creation \ --set inputType=object \ --set type=noencode \ --set type=multiprimitives \ --set type=array \ --set n=4000000 \ url
InputBeforeAfterImprovement
String values2.925M ops/s3.270M ops/s+11.80%
Mixed primitive values2.840M ops/s3.093M ops/s+8.89%
Array values2.055M ops/s2.250M ops/s+9.50%

For ten-property records, a separate GC diagnostic measured:

  • allocated memory: 3,016.5 β†’ 2,076.1 bytes/op (-31.2%)
  • scavenges: 724 β†’ 498 (-31.2%)
  • cumulative GC pause: 32.29 β†’ 22.50 ms (-30.3%)

A conversion-heavy TCP loopback benchmark improved from 857k to 1.14M frames/s (+33%). This only demonstrates the cumulative effect when URLSearchParams construction is hot; it is not a general TCP performance claim.

The malformed-key fallback measured 544.4 β†’ 550.9 ns/op, around 1.2% slower and within the observed noise. This rare path now pays for creating and backfilling the map.

Tests

The focused constructor test, the URLSearchParams fast-path test, and the URL
WPT suite pass.

@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

Review requested:

  • @nodejs/url

@nodejs-github-botnodejs-github-bot added needs-ci PRs that need a full CI run. whatwg-url Issues and PRs related to the WHATWG URL implementation. labels Sep 4, 2026
Object property keys are already unique unless USVString normalization
changes a key. Avoid allocating and updating a SafeMap for ordinary
record keys. Create and backfill it when normalization first changes a
key.
This improves object construction by 10% to 25% in the measured cases
and reduces allocation by about 31% for a ten-property record.
Assisted-by: Amp
Signed-off-by: Romain Lanz <romain.lanz@pm.me>
@RomainLanz
RomainLanzforce-pushed the perf/urlsearchparams-lazy-map branch from 3f30ad2 to 2fb7b16CompareSeptember 4, 2026 19:06
@codecov

codecovBot commented Sep 4, 2026

Copy link
Copy Markdown

Codecov Report

βœ… All modified and coverable lines are covered by tests.
βœ… Project coverage is 90.13%. Comparing base (1e9fd95) to head (2fb7b16).
⚠️ Report is 8 commits behind head on main.

Additional details and impacted files
@@ Coverage Diff @@## main #65801 +/- ##
==========================================
- Coverage 90.17% 90.13% -0.04% 
==========================================
Files 769 769 Lines 261448 261638 +190 Branches 49674 49673 -1 ==========================================
+ Hits 235759 235831 +72 - Misses 16736 16812 +76 - Partials 8953 8995 +42 
Files with missing linesCoverage Ξ”
lib/internal/url.js93.31% <100.00%> (+0.04%)⬆️

... and 39 files with indirect coverage changes

πŸš€ New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • πŸ“¦ JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs-ciPRs that need a full CI run.whatwg-urlIssues and PRs related to the WHATWG URL implementation.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@RomainLanz@nodejs-github-bot