-
Notifications
You must be signed in to change notification settings - Fork 539
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fetch: replace HeadersList Array inheritance to Map #1309
Conversation
From my investigations the Map wasn't as good for reading the header values (as in per spec you must return the values in sorted order) but I do know v8 is always making magic happen; especially with objects like Map. Wouldn't be shocked if your new benchmark results are even better perf wise. One thing to keep in mind is memory footprint too; a flat array of strings should be more efficient space wise but it's a hard one to verify as well. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lgtm
Can you point me to this statement in the spec? Yeah, in my benchmarks it was better than Array implementation, mostly due to the fact of needing |
https://fetch.spec.whatwg.org/#headers-class
|
Just run a server that logs the incoming request's headers: The headers are not alphabetically ordered. Also I don't see any clear mention in the specs that the headers should be alphabetically ordered. BTW, an example of "sorted" array: I expect that it (undici/fetch) will allow to a user decide the order of the headers, if the user will decide to overwrite the default headers, or add new ones. |
Yea. I think the spec only requires it to be sorted in the Headers class iterator member function. |
I have adjusted, but still non-optimal. I'll implement a orderedmap soon. |
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #1309 +/- ##
==========================================
- Coverage 94.16% 94.10% -0.06%
==========================================
Files 45 45
Lines 4112 4189 +77
==========================================
+ Hits 3872 3942 +70
- Misses 240 247 +7 ☔ View full report in Codecov by Sentry. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it make sense to cache the sorted list?
Yes, I thought about it yesterday. However, I'm thinking of a solution for an insertion sort (like the one before, but with less overhead). Also, it's better(for the memory footprint) to have a flag telling if the list is currently sorted. Example: if (this[kSorted]) {
listFreezed = new HeadersList(this[kHeadersList])
} else {
this[kHeadersList] = new HeadersList([...this[kHeadersList]].sort())
this[kSorted] = true
listFreezed = new HeadersList(this[kHeadersList])
}
for (const val of listFreezed.values()) {
yield val
} |
@RafaelGSS Please don't stop working on this. 😄 I was looking forward to having this fixed. |
Sure thing! I just have some priorities for this week. I'll be back on it soon as possible 😄 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Getting close!
This should not require any (almost) changes outside of the headers stuff, e.g. replacing append
with set
is wrong and unnecessary.
Also the store for the headers should never be sorted. You would need to create a separate sorted list copy used only for iteration. We want to send the headers to the server as specified by the users (i.e. without sorting).
Good spot. Working on this. |
@ronag After all the changes, looks like we will get no performance improvement. The Benchmark CI looks not consistent, so, I'll perform those tests in my dedicated machine and share the results soon. In case of no performance improvements, I'm not sure how to proceed with that. Maybe just close it? |
This branch:
TL;DR: The performance is the same, we should just choose the best implementation. |
I still think this PR is good since it's more correct in terms of the ordering @AlttiRi mentions. |
lib/fetch/headers.js
Outdated
const normalizedValue = normalizeAndValidateHeaderValue(name, value) | ||
* [Symbol.iterator] () { | ||
if (!this[kHeadersSortedMap]) { | ||
this.sortMap() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you can inline sortMap?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You mean?
if (!this[kHeadersSortedMap]) this.sortMap()
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lgtm, the perf boost would bave been nice
@RafaelGSS I helped a little. Hope you don't mind. PTAL. |
LGTM overall. |
This is a breaking change, right? |
* update: uses Map over Array in HeadersList * fix: create empty HeaderList * update(fetch/headers): sort keys before returning * update: remove binarySearch method * perf: use headersList.set to bypass validation * update: remove comment * fix: lint * update(headers): create a new headers map for sorting * update: remove kSorted * update: replace .set to .append * fixes * fix: lint Co-authored-by: Robert Nagy <ronagy@icloud.com>
* update: uses Map over Array in HeadersList * fix: create empty HeaderList * update(fetch/headers): sort keys before returning * update: remove binarySearch method * perf: use headersList.set to bypass validation * update: remove comment * fix: lint * update(headers): create a new headers map for sorting * update: remove kSorted * update: replace .set to .append * fixes * fix: lint Co-authored-by: Robert Nagy <ronagy@icloud.com>
* update: uses Map over Array in HeadersList * fix: create empty HeaderList * update(fetch/headers): sort keys before returning * update: remove binarySearch method * perf: use headersList.set to bypass validation * update: remove comment * fix: lint * update(headers): create a new headers map for sorting * update: remove kSorted * update: replace .set to .append * fixes * fix: lint Co-authored-by: Robert Nagy <ronagy@icloud.com>
This PR replaces the
HeadersList
Array inheritance to Map, which basically is more suitable for headers use case.It also increases the performance by 10% (it's not done yet, so it might be more/less).