Skip to content

Commit e6c9c8e

Browse files
jasnellnodejs-github-bot
authored andcommitted
stream: construct stream/iter byte view snapshots and batch entries
createBatchEntry() and recordChunk() snapshot every chunk in a seven-property `{ __proto__: null, ... }` literal, and every batch gets a `{ __proto__: null, views, byteLength }` record. V8 creates such literals in dictionary mode, which is expensive for objects created for every chunk. Create them with constructors whose prototype is an empty, frozen, null-prototype object instead, as for IterResult. They have fast properties and a single shape, and are only used internally. Writing 1e6 16-byte chunks into a push() stream and reading them back is about 6x faster with writeSync(), 3.7x faster with writevSync() (4 chunks per call) and 1.7x faster with string chunks. benchmark/streams/iter-throughput-share-sync.js improves by 67-93%, iter-throughput-share.js by 4-8%, and iter-throughput-broadcast.js with 4 consumers by 8%. pipeTo() with small chunks is about 1.5x faster. Assisted-by: OpenCode Signed-off-by: James M Snell <jasnell@gmail.com> PR-URL: #66648 Reviewed-By: Trivikram Kamat <trivikr.dev@gmail.com> Reviewed-By: Gürgün Dayıoğlu <hey@gurgun.day>
1 parent 724190c commit e6c9c8e

1 file changed

Lines changed: 28 additions & 17 deletions

File tree

‎lib/internal/streams/iter/utils.js‎

Lines changed: 28 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -217,23 +217,35 @@ function toUint8Array(chunk) {
217217
return chunk;
218218
}
219219

220+
// Byte view snapshots and batch entries are created for every chunk and
221+
// every batch, so like IterResult they are constructed rather than created
222+
// as `{ __proto__: null, ... }` literals (which are dictionary-mode objects),
223+
// and their prototype is an empty null-prototype object.
224+
function ByteViewSnapshot(value, buffer, sharedBufferView) {
225+
this.value = value;
226+
this.buffer = buffer;
227+
this.bufferByteLength = sharedBufferView === undefined ?
228+
ArrayBufferPrototypeGetByteLength(buffer) :
229+
TypedArrayPrototypeGetByteLength(sharedBufferView);
230+
this.byteLength = TypedArrayPrototypeGetByteLength(value);
231+
this.byteOffset = TypedArrayPrototypeGetByteOffset(value);
232+
this.detached = sharedBufferView === undefined &&
233+
ArrayBufferPrototypeGetDetached(buffer);
234+
this.sharedBufferView = sharedBufferView;
235+
}
236+
ByteViewSnapshot.prototype = ObjectFreeze({ __proto__: null });
237+
238+
function BatchEntry(views, byteLength) {
239+
this.views = views;
240+
this.byteLength = byteLength;
241+
}
242+
BatchEntry.prototype = ObjectFreeze({ __proto__: null });
243+
220244
function snapshotByteView(value) {
221245
const buffer = TypedArrayPrototypeGetBuffer(value);
222246
const sharedBufferView = isSharedArrayBuffer(buffer) ?
223247
new Uint8Array(buffer) : undefined;
224-
return {
225-
__proto__: null,
226-
value,
227-
buffer,
228-
bufferByteLength: sharedBufferView === undefined ?
229-
ArrayBufferPrototypeGetByteLength(buffer) :
230-
TypedArrayPrototypeGetByteLength(sharedBufferView),
231-
byteLength: TypedArrayPrototypeGetByteLength(value),
232-
byteOffset: TypedArrayPrototypeGetByteOffset(value),
233-
detached: sharedBufferView === undefined &&
234-
ArrayBufferPrototypeGetDetached(buffer),
235-
sharedBufferView,
236-
};
248+
return new ByteViewSnapshot(value, buffer, sharedBufferView);
237249
}
238250

239251
function validateByteView(snapshot) {
@@ -325,7 +337,7 @@ function createBatchEntry(chunks) {
325337
views[i] = view;
326338
byteLength += view.byteLength;
327339
}
328-
return { __proto__: null, views, byteLength };
340+
return new BatchEntry(views, byteLength);
329341
}
330342

331343
/**
@@ -345,15 +357,14 @@ function splitBatchEntry(entry, limit) {
345357
for (let i = 0; i < views.length; i++) {
346358
const view = views[i];
347359
if (current.length > 0 && byteLength + view.byteLength >= limit) {
348-
ArrayPrototypePush(entries,
349-
{ __proto__: null, views: current, byteLength });
360+
ArrayPrototypePush(entries, new BatchEntry(current, byteLength));
350361
current = [];
351362
byteLength = 0;
352363
}
353364
ArrayPrototypePush(current, view);
354365
byteLength += view.byteLength;
355366
}
356-
ArrayPrototypePush(entries, { __proto__: null, views: current, byteLength });
367+
ArrayPrototypePush(entries, new BatchEntry(current, byteLength));
357368
return entries;
358369
}
359370

0 commit comments

Comments
 (0)