1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-12-22 15:24:46 -05:00

perf(ext/streams): optimize async iterator (#20541)

This PR optimizes `ReadableStream` async iterator

### Benchmarks

```js
Deno.bench("Stream - iterator", async () => {
  const stream = new ReadableStream({
    start(controller) {
      controller.enqueue(new Uint8Array([97]));
      controller.enqueue(new Uint8Array([97]));
      controller.close();
    },
  });

  for await (const chunk of stream) {}
});
```

**main**

`2 chunks`
```
cpu: 13th Gen Intel(R) Core(TM) i9-13900H
runtime: deno 1.36.4 (x86_64-unknown-linux-gnu)

benchmark              time (avg)        iter/s             (min … max)       p75       p99      p995
----------------------------------------------------------------------- -----------------------------
Stream - iterator      12.45 µs/iter      80,295.5   (10.5 µs … 281.12 µs)  12.13 µs  26.71 µs  33.63 µs
```
`20 chunks`

```
benchmark              time (avg)        iter/s             (min … max)       p75       p99      p995
----------------------------------------------------------------------- -----------------------------
Stream - iterator      32.99 µs/iter      30,312.2    (28.13 µs … 1.21 ms)   31.8 µs  81.82 µs 179.93 µs
```
---

**this PR**

`2 chunks`
```
cpu: 13th Gen Intel(R) Core(TM) i9-13900H
runtime: deno 1.36.4 (x86_64-unknown-linux-gnu)

benchmark              time (avg)        iter/s             (min … max)       p75       p99      p995
----------------------------------------------------------------------- -----------------------------
Stream - iterator       9.37 µs/iter     106,700.8   (8.35 µs … 730.71 µs)   9.15 µs  13.12 µs  18.17 µs
```
`20 chunks`
```
benchmark              time (avg)        iter/s             (min … max)       p75       p99      p995
----------------------------------------------------------------------- -----------------------------
Stream - iterator      16.59 µs/iter      60,270.0    (12.08 µs … 1.37 ms)  15.06 µs  83.03 µs 123.52 µs
```
This commit is contained in:
Marcos Casagrande 2023-09-17 17:54:40 +02:00 committed by GitHub
parent fa18878f54
commit 4960b6659c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -53,10 +53,8 @@ const {
NumberIsInteger, NumberIsInteger,
NumberIsNaN, NumberIsNaN,
ObjectCreate, ObjectCreate,
ObjectDefineProperties,
ObjectDefineProperty, ObjectDefineProperty,
ObjectGetPrototypeOf, ObjectGetPrototypeOf,
ObjectPrototype,
ObjectPrototypeIsPrototypeOf, ObjectPrototypeIsPrototypeOf,
ObjectSetPrototypeOf, ObjectSetPrototypeOf,
Promise, Promise,
@ -4651,26 +4649,6 @@ function writableStreamUpdateBackpressure(stream, backpressure) {
stream[_backpressure] = backpressure; stream[_backpressure] = backpressure;
} }
/**
* @template T
* @param {T} value
* @param {boolean} done
* @returns {IteratorResult<T>}
*/
function createIteratorResult(value, done) {
const result = ObjectCreate(ObjectPrototype);
ObjectDefineProperties(result, {
value: { value, writable: true, enumerable: true, configurable: true },
done: {
value: done,
writable: true,
enumerable: true,
configurable: true,
},
});
return result;
}
/** @type {AsyncIterator<unknown, unknown>} */ /** @type {AsyncIterator<unknown, unknown>} */
const asyncIteratorPrototype = ObjectGetPrototypeOf(AsyncGeneratorPrototype); const asyncIteratorPrototype = ObjectGetPrototypeOf(AsyncGeneratorPrototype);
@ -4685,7 +4663,7 @@ const readableStreamAsyncIteratorPrototype = ObjectSetPrototypeOf({
const reader = this[_reader]; const reader = this[_reader];
function nextSteps() { function nextSteps() {
if (reader[_iteratorFinished]) { if (reader[_iteratorFinished]) {
return PromiseResolve(createIteratorResult(undefined, true)); return PromiseResolve({ value: undefined, done: true });
} }
if (reader[_stream] === undefined) { if (reader[_stream] === undefined) {
@ -4701,11 +4679,11 @@ const readableStreamAsyncIteratorPrototype = ObjectSetPrototypeOf({
/** @type {ReadRequest} */ /** @type {ReadRequest} */
const readRequest = { const readRequest = {
chunkSteps(chunk) { chunkSteps(chunk) {
promise.resolve(createIteratorResult(chunk, false)); promise.resolve({ value: chunk, done: false });
}, },
closeSteps() { closeSteps() {
readableStreamDefaultReaderRelease(reader); readableStreamDefaultReaderRelease(reader);
promise.resolve(createIteratorResult(undefined, true)); promise.resolve({ value: undefined, done: true });
}, },
errorSteps(e) { errorSteps(e) {
readableStreamDefaultReaderRelease(reader); readableStreamDefaultReaderRelease(reader);
@ -4718,7 +4696,7 @@ const readableStreamAsyncIteratorPrototype = ObjectSetPrototypeOf({
reader[_iteratorNext] = null; reader[_iteratorNext] = null;
if (result.done === true) { if (result.done === true) {
reader[_iteratorFinished] = true; reader[_iteratorFinished] = true;
return createIteratorResult(undefined, true); return { value: undefined, done: true };
} }
return result; return result;
}, (reason) => { }, (reason) => {
@ -4743,12 +4721,12 @@ const readableStreamAsyncIteratorPrototype = ObjectSetPrototypeOf({
const reader = this[_reader]; const reader = this[_reader];
const returnSteps = () => { const returnSteps = () => {
if (reader[_iteratorFinished]) { if (reader[_iteratorFinished]) {
return PromiseResolve(createIteratorResult(arg, true)); return PromiseResolve({ value: arg, done: true });
} }
reader[_iteratorFinished] = true; reader[_iteratorFinished] = true;
if (reader[_stream] === undefined) { if (reader[_stream] === undefined) {
return PromiseResolve(createIteratorResult(undefined, true)); return PromiseResolve({ value: undefined, done: true });
} }
assert(reader[_readRequests].length === 0); assert(reader[_readRequests].length === 0);
if (this[_preventCancel] === false) { if (this[_preventCancel] === false) {
@ -4757,7 +4735,7 @@ const readableStreamAsyncIteratorPrototype = ObjectSetPrototypeOf({
return result; return result;
} }
readableStreamDefaultReaderRelease(reader); readableStreamDefaultReaderRelease(reader);
return PromiseResolve(createIteratorResult(undefined, true)); return PromiseResolve({ value: undefined, done: true });
}; };
const returnPromise = reader[_iteratorNext] const returnPromise = reader[_iteratorNext]
@ -4765,7 +4743,7 @@ const readableStreamAsyncIteratorPrototype = ObjectSetPrototypeOf({
: returnSteps(); : returnSteps();
return PromisePrototypeThen( return PromisePrototypeThen(
returnPromise, returnPromise,
() => createIteratorResult(arg, true), () => ({ value: arg, done: true }),
); );
}, },
}, asyncIteratorPrototype); }, asyncIteratorPrototype);