Repository navigation
Performance issue with URL.searchParams.append for large numbers of params #51518
Description
Activity
- addedperformanceIssues and PRs related to the performance of Node.js.Issues and PRs related to the performance of Node.js.
on Jan 19, 2024 (unrelated to Node.js, but bonus points to anyone that feels like surfacing a similar fix with browser engines, as they seem to suffer from the same bottleneck)
As I understand it currently,
URLstores its entire value in#context.hrefas a single string (and associated pointers for search etc.). When instantiated,URLdoes not have an associatedURLSearchParams. If, and only if, a user accessessearchParamsonURL, then aURLSearchParamsis instantiated for theURL. However,URLstill relies on its own#context.hrefvalue always, and so the data inURLSearchParamsis essentially a duplicate, and any update to it needs to call thesearchsetter onURLso that#context.hrefcan be updated properly.Thinking through this, I think the approach to fixing this might be:
- Remove the
URL#contextfromURLSearchParams, so it is a properly independent data store - Update the
search+hrefgetters inURLto interface withsearchParamsif it exists - Update the
search+hrefsetters inURLto interface withsearchParamsif it exists, and not pass any search to the internal#context.href, so it is just stored insearchParams(or, rather, update#updateContextto slice the returnedhrefand not store the search index whensearchParamsexists) - Update
searchParamsso that when it instantiatesURLSeachParams, it also removes any search from the internal#context.href, so it is just stored insearchParams(probably via the same#updateContextmodification)
This does mean that the internal
#context.hrefvalue ofURLwill not contain the search string oncesearchParamshas been accessed, but I think that is probably the best thing to do so there is no risk of future confusion with the state of that being potentially out of sync withsearchParams.- Remove the
@anonrig @nodejs/performance
- added a commit that references this issue
on Jan 24, 2024 - added a commit that references this issue
on Feb 9, 2024 - added a commit that references this issue
on Feb 15, 2024
Version
21.6.0
Platform
Darwin [snip] 23.2.0 Darwin Kernel Version 23.2.0: Wed Nov 15 21:53:18 PST 2023; root:xnu-10002.61.3~2/RELEASE_ARM64_T6000 arm64
Subsystem
No response
What steps will reproduce the bug?
Using
URL.searchParams.append:vs. using
URLSearchParams.append:How often does it reproduce? Is there a required condition?
No response
What is the expected behavior? Why is that the expected behavior?
No response
What do you see instead?
The
URL.searchParams.appendsnippet with 50,000 params being append takes 2-3 minutes to run on an M1, which is a ridiculous amount of time. The equivalent snippet withURLSearchParams.appendtakes 50-100 milliseconds to run.Additional information
I talked through this performance issue with Yagiz on Twitter and got to what appears to be the cause of the issue, every time
URL.searchParams.appendis called, the whole URL essentially gets stringified to update properties onURLitself: https://twitter.com/yagiznizipli/status/1748151781534650545I believe it to be this call that is the cause:
node/lib/internal/url.js
Lines 478 to 480 in eb4432c
With
#contextbeing set here:node/lib/internal/url.js
Lines 1023 to 1024 in eb4432c
And the
#context.searchsetter being here:node/lib/internal/url.js
Lines 1012 to 1017 in eb4432c
I'm unsure whether the performance issue is with the
toStringcall inURLSearchParams, or whether it is within the logic of the#context.searchsetter. I've not worked with Node.js source before, so not sure where to start on profiling that, or submitting a fix (hence opening this issue to track it for anyone to grab).I suspect that an "easy" fix either way is to update
URLSearchParamssuch that it doesn't write back toURL, and instead update the relevant bits ofURL(href,search, etc.) to essentially call the logic that is currently in the#context.searchsetter on-demand as getters when needed, ifURLSearchParamshas been created for theURL.