Skip to content

Commit 3ac0b42

Browse files
bakkotaduh95
authored andcommitted
benchmark: add URLSearchParams iterator consumers benchmark
Assisted-by: claude:opus-5.5 Signed-off-by: Kevin Gibbons <bakkot@gmail.com> PR-URL: #66837 Reviewed-By: Jordan Harband <ljharb@gmail.com> Reviewed-By: LiviaMedeiros <livia@cirno.name>
1 parent ae76a8b commit 3ac0b42

12 files changed

Lines changed: 159 additions & 102 deletions
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
'use strict';
2+
const common = require('../common.js');
3+
const assert = require('assert');
4+
5+
// Consumes a URLSearchParams iterator with builtins that have fast paths for
6+
// iterator result objects (Array.from, Map constructor, Object.fromEntries),
7+
// plus for-of for comparison.
8+
const bench = common.createBenchmark(main, {
9+
method: ['Array.from', 'Map', 'Object.fromEntries', 'for-of'],
10+
n: [1e6],
11+
});
12+
13+
const str = 'one=single&two=first&three=first&two=2nd&three=2nd&three=3rd';
14+
15+
function main({ method, n }) {
16+
const params = new URLSearchParams(str);
17+
let noDead;
18+
19+
switch (method) {
20+
case 'Array.from':
21+
bench.start();
22+
for (let i = 0; i < n; i += 1)
23+
noDead = Array.from(params);
24+
bench.end(n);
25+
assert.strictEqual(noDead.length, 6);
26+
break;
27+
case 'Map':
28+
bench.start();
29+
for (let i = 0; i < n; i += 1)
30+
noDead = new Map(params);
31+
bench.end(n);
32+
assert.strictEqual(noDead.size, 3);
33+
break;
34+
case 'Object.fromEntries':
35+
bench.start();
36+
for (let i = 0; i < n; i += 1)
37+
noDead = Object.fromEntries(params);
38+
bench.end(n);
39+
assert.strictEqual(noDead.three, '3rd');
40+
break;
41+
case 'for-of':
42+
bench.start();
43+
for (let i = 0; i < n; i += 1) {
44+
for (const pair of params)
45+
noDead = pair;
46+
}
47+
bench.end(n);
48+
assert.strictEqual(noDead[1], '3rd');
49+
break;
50+
default:
51+
throw new Error(`Unknown method ${method}`);
52+
}
53+
}

‎lib/eslint.config_partial.mjs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -435,7 +435,7 @@ export default [
435435
'node-core/alphabetize-errors': 'error',
436436
'node-core/alphabetize-primordials': 'error',
437437
'node-core/avoid-prototype-pollution': 'error',
438-
'node-core/iterator-result-done-first': 'error',
438+
'node-core/iterator-result-value-first': 'error',
439439
'node-core/lowercase-name-for-primitive': 'error',
440440
'node-core/non-ascii-character': 'error',
441441
'node-core/no-array-destructuring': 'error',

‎lib/events.js‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1043,7 +1043,7 @@ async function once(emitter, name, options = kEmptyObject) {
10431043
}
10441044

10451045
function createIterResult(value, done) {
1046-
return { done, value };
1046+
return { value, done };
10471047
}
10481048

10491049
function eventTargetAgnosticRemoveListener(emitter, name, listener, flags) {

‎lib/internal/fs/promises.js‎

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -672,7 +672,7 @@ if (getOptionValue('--experimental-stream-iter')) {
672672
__proto__: null,
673673
next() {
674674
if (done) {
675-
return { done: true, value: undefined };
675+
return { value: undefined, done: true };
676676
}
677677
if (!started) {
678678
// Lock lazily, on the first read, so that an iterable that is
@@ -684,7 +684,7 @@ if (getOptionValue('--experimental-stream-iter')) {
684684
if (remaining === 0) {
685685
done = true;
686686
cleanup();
687-
return { done: true, value: undefined };
687+
return { value: undefined, done: true };
688688
}
689689
const toRead = remaining > 0 ?
690690
MathMin(readSize, remaining) : readSize;
@@ -700,20 +700,20 @@ if (getOptionValue('--experimental-stream-iter')) {
700700
if (bytesRead === 0) {
701701
done = true;
702702
cleanup();
703-
return { done: true, value: undefined };
703+
return { value: undefined, done: true };
704704
}
705705
if (pos >= 0) pos += bytesRead;
706706
if (remaining > 0) remaining -= bytesRead;
707707
const chunk = bytesRead < toRead ?
708708
buf.subarray(0, bytesRead) : buf;
709-
return { done: false, value: [chunk] };
709+
return { value: [chunk], done: false };
710710
},
711711
return() {
712712
if (!done) {
713713
done = true;
714714
if (started) cleanup();
715715
}
716-
return { done: true, value: undefined };
716+
return { value: undefined, done: true };
717717
},
718718
};
719719
},

‎lib/internal/source_map/source_map_cache_map.js‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ class SourceMapCacheMap {
104104
const moduleInstance = weakRef.deref();
105105
if (moduleInstance == null) return next();
106106
const value = moduleInstance[source_map_data_private_symbol];
107-
return { done: false, value: [key, value] };
107+
return { value: [key, value], done: false };
108108
};
109109

110110
return {

‎lib/internal/streams/readable.js‎

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1656,7 +1656,7 @@ function createAsyncIterator(stream, options) {
16561656
function onChunkFulfilled(value) {
16571657
inFlight = false;
16581658
if (queue !== null) drain();
1659-
return { done: false, value };
1659+
return { value, done: false };
16601660
}
16611661

16621662
function onChunkRejected(err) {
@@ -1679,7 +1679,7 @@ function createAsyncIterator(stream, options) {
16791679
if (typeof then === 'function') {
16801680
FunctionPrototypeCall(then, chunk, (value) => {
16811681
inFlight = false;
1682-
resolve({ done: false, value });
1682+
resolve({ value, done: false });
16831683
if (queue !== null) drain();
16841684
}, (err) => {
16851685
inFlight = false;
@@ -1689,7 +1689,7 @@ function createAsyncIterator(stream, options) {
16891689
return;
16901690
}
16911691
inFlight = false;
1692-
resolve({ done: false, value: chunk });
1692+
resolve({ value: chunk, done: false });
16931693
if (queue !== null) drain();
16941694
} else if (error) {
16951695
inFlight = false;
@@ -1698,7 +1698,7 @@ function createAsyncIterator(stream, options) {
16981698
} else if (error === null) {
16991699
inFlight = false;
17001700
finalize();
1701-
resolve({ done: true, value: undefined });
1701+
resolve({ value: undefined, done: true });
17021702
if (queue !== null) drain();
17031703
} else {
17041704
// No data buffered yet; wait for 'readable' or end-of-stream and
@@ -1709,7 +1709,7 @@ function createAsyncIterator(stream, options) {
17091709

17101710
function processNext(resolve, reject) {
17111711
if (completed) {
1712-
resolve({ done: true, value: undefined });
1712+
resolve({ value: undefined, done: true });
17131713
return;
17141714
}
17151715
if (!started) start();
@@ -1726,7 +1726,7 @@ function createAsyncIterator(stream, options) {
17261726
completed = true;
17271727
}
17281728
}
1729-
resolve({ done: true, value });
1729+
resolve({ value, done: true });
17301730
}
17311731

17321732
function processThrow(err, reject) {
@@ -1755,15 +1755,15 @@ function createAsyncIterator(stream, options) {
17551755
return FunctionPrototypeCall(
17561756
then, chunk, onChunkFulfilled, onChunkRejected);
17571757
}
1758-
return PromiseResolve({ done: false, value: chunk });
1758+
return PromiseResolve({ value: chunk, done: false });
17591759
}
17601760
if (error) {
17611761
finalize();
17621762
return PromiseReject(error);
17631763
}
17641764
if (error === null) {
17651765
finalize();
1766-
return PromiseResolve({ done: true, value: undefined });
1766+
return PromiseResolve({ value: undefined, done: true });
17671767
}
17681768
// No data buffered yet; wait for 'readable' or end-of-stream.
17691769
inFlight = true;
@@ -1776,7 +1776,7 @@ function createAsyncIterator(stream, options) {
17761776
queue ??= new FixedQueue();
17771777
queue.push({ __proto__: null, type: 'next', value: undefined, resolve, reject });
17781778
} else {
1779-
resolve({ done: true, value: undefined });
1779+
resolve({ value: undefined, done: true });
17801780
}
17811781
});
17821782
},

‎lib/internal/url.js‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -278,8 +278,8 @@ class URLSearchParamsIterator {
278278
const len = values.length;
279279
if (index >= len) {
280280
return {
281-
done: true,
282281
value: undefined,
282+
done: true,
283283
};
284284
}
285285

@@ -297,8 +297,8 @@ class URLSearchParamsIterator {
297297
}
298298

299299
return {
300-
done: false,
301300
value: result,
301+
done: false,
302302
};
303303
}
304304

‎lib/internal/vfs/watcher.js‎

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -602,7 +602,7 @@ class VFSWatchAsyncIterable {
602602
const event = { eventType, filename };
603603
if (this.#pendingResolvers.length > 0) {
604604
const { resolve } = this.#pendingResolvers.shift();
605-
resolve({ done: false, value: event });
605+
resolve({ value: event, done: false });
606606
} else if (this.#pendingEvents.length < kMaxPendingEvents) {
607607
ArrayPrototypePush(this.#pendingEvents, event);
608608
}
@@ -614,7 +614,7 @@ class VFSWatchAsyncIterable {
614614
// Resolve any pending iterators
615615
while (this.#pendingResolvers.length > 0) {
616616
const { resolve } = this.#pendingResolvers.shift();
617-
resolve({ done: true, value: undefined });
617+
resolve({ value: undefined, done: true });
618618
}
619619
});
620620

@@ -664,12 +664,12 @@ class VFSWatchAsyncIterable {
664664
}
665665

666666
if (this.#closed) {
667-
return PromiseResolve({ done: true, value: undefined });
667+
return PromiseResolve({ value: undefined, done: true });
668668
}
669669

670670
if (this.#pendingEvents.length > 0) {
671671
const event = this.#pendingEvents.shift();
672-
return PromiseResolve({ done: false, value: event });
672+
return PromiseResolve({ value: event, done: false });
673673
}
674674

675675
return new Promise((resolve, reject) => {
@@ -683,7 +683,7 @@ class VFSWatchAsyncIterable {
683683
*/
684684
return() {
685685
this.#watcher.close();
686-
return PromiseResolve({ done: true, value: undefined });
686+
return PromiseResolve({ value: undefined, done: true });
687687
}
688688

689689
/**
@@ -693,7 +693,7 @@ class VFSWatchAsyncIterable {
693693
*/
694694
throw(error) {
695695
this.#watcher.close();
696-
return PromiseResolve({ done: true, value: undefined });
696+
return PromiseResolve({ value: undefined, done: true });
697697
}
698698
}
699699

‎lib/internal/webstreams/readablestream.js‎

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -712,14 +712,14 @@ class ReadableStreamAsyncIteratorReadRequest {
712712

713713
[kChunk](chunk) {
714714
this.current = undefined;
715-
this.promise.resolve({ done: false, value: chunk });
715+
this.promise.resolve({ value: chunk, done: false });
716716
}
717717

718718
[kClose]() {
719719
this.current = undefined;
720720
this.done = true;
721721
readableStreamReaderGenericRelease(this.reader);
722-
this.promise.resolve({ done: true, value: undefined });
722+
this.promise.resolve({ value: undefined, done: true });
723723
}
724724

725725
[kError](error) {
@@ -735,7 +735,7 @@ class ReadableStreamAsyncIteratorReadRequest {
735735
// promise allocations.
736736
function readableStreamAsyncIteratorNextSteps(state) {
737737
if (state.done)
738-
return PromiseResolve({ done: true, value: undefined });
738+
return PromiseResolve({ value: undefined, done: true });
739739

740740
const reader = state.reader;
741741
if (reader[kState].stream === undefined) {
@@ -753,7 +753,7 @@ function readableStreamAsyncIteratorNextSteps(state) {
753753
// Not an async function either: the cancel path settles one microtask
754754
// after the cancel promise, exactly as `await` would.
755755
function readableStreamAsyncIteratorReturnSteps(state, value) {
756-
const iterResult = { done: true, value };
756+
const iterResult = { value, done: true };
757757
if (state.done)
758758
return PromiseResolve(iterResult);
759759
state.done = true;
@@ -841,16 +841,16 @@ class ReadableStreamAsyncIterator {
841841
readableStreamDefaultControllerPull(controller);
842842
}
843843

844-
return PromiseResolve({ done: false, value: chunk });
844+
return PromiseResolve({ value: chunk, done: false });
845845
}
846846
} else if (controller[kState].queueTotalSize > 0) {
847847
// Byte controller with buffered data: same shape as above via
848848
// the queue-filled arm of the byte controller's pull steps.
849849
stream[kState].disturbed = true;
850850
return PromiseResolve({
851-
done: false,
852-
853851
value: readableByteStreamControllerDequeueChunk(controller),
852+
853+
done: false,
854854
});
855855
}
856856
}
@@ -891,11 +891,11 @@ class DefaultReadRequest {
891891
}
892892

893893
[kChunk](value) {
894-
this[kState].resolve({ done: false, value });
894+
this[kState].resolve({ value, done: false });
895895
}
896896

897897
[kClose]() {
898-
this[kState].resolve({ done: true, value: undefined });
898+
this[kState].resolve({ value: undefined, done: true });
899899
}
900900

901901
[kError](error) {
@@ -936,11 +936,11 @@ class ReadIntoRequest {
936936
}
937937

938938
[kChunk](value) {
939-
this[kState].resolve({ done: false, value });
939+
this[kState].resolve({ value, done: false });
940940
}
941941

942942
[kClose](value) {
943-
this[kState].resolve({ done: true, value });
943+
this[kState].resolve({ value, done: true });
944944
}
945945

946946
[kError](error) {
@@ -1009,17 +1009,17 @@ class ReadableStreamDefaultReader {
10091009
readableStreamDefaultControllerPull(controller);
10101010
}
10111011

1012-
return PromiseResolve({ done: false, value: chunk });
1012+
return PromiseResolve({ value: chunk, done: false });
10131013
}
10141014
} else if (controller[kState].queueTotalSize > 0) {
10151015
// Byte controller with buffered data: mirror the queue-filled arm
10161016
// of its pull steps (which never consults pendingPullIntos) minus
10171017
// the read request.
10181018
stream[kState].disturbed = true;
10191019
return PromiseResolve({
1020-
done: false,
1021-
10221020
value: readableByteStreamControllerDequeueChunk(controller),
1021+
1022+
done: false,
10231023
});
10241024
}
10251025
}

0 commit comments

Comments
 (0)