Skip to content

stream: fast path single-destination pipe#63592

Open
trivikr wants to merge 4 commits into
nodejs:mainfrom
trivikr:stream-pipe-hot-path-listener-overhead
Open

stream: fast path single-destination pipe#63592
trivikr wants to merge 4 commits into
nodejs:mainfrom
trivikr:stream-pipe-hot-path-listener-overhead

Conversation

@trivikr
Copy link
Copy Markdown
Member

@trivikr trivikr commented May 27, 2026

Avoid EventEmitter dispatch for the internal readable.pipe() data handler
when it is the only data listener on the source stream.

If any additional data listener is present, the stream falls back to normal
data event emission so listener ordering is preserved.

Benchmark:

                          confidence improvement accuracy (*)   (**)  (***)
streams/pipe.js n=5000000        ***     11.96 %       +/-2.00% +/-2.66% +/-3.47%

Assisted-by: openai:gpt-5.5

@nodejs-github-bot
Copy link
Copy Markdown
Collaborator

Review requested:

  • @nodejs/streams

@nodejs-github-bot nodejs-github-bot added needs-ci PRs that need a full CI run. stream Issues and PRs related to the stream subsystem. labels May 27, 2026
Copy link
Copy Markdown
Member

@ronag ronag left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks to me like it will break stuff. What happens if a data listener is added after pipe? Can you add a test for that.

@trivikr
Copy link
Copy Markdown
Member Author

trivikr commented May 27, 2026

What happens if a data listener is added after pipe?

If a data listener is added after pipe(), the fast path stops applying because _events.data is no longer the single internal pipe handler. Subsequent chunks go through normal stream.emit('data'), preserving EventEmitter listener order.

Can you add a test for that?

Added a test in 3184889 (this PR)

The added test starts with r.pipe(w), then adds a data listener during the first destination write(). It asserts the listener does not see the in-flight first chunk, but does see the second chunk after the pipe write, matching normal EventEmitter behavior.

@trivikr trivikr requested a review from ronag May 27, 2026 05:55
@trivikr trivikr force-pushed the stream-pipe-hot-path-listener-overhead branch from 2d523a1 to 3184889 Compare May 27, 2026 06:15
@ronag ronag requested a review from mcollina May 27, 2026 06:34
@ronag ronag added the request-ci Add this label to start a Jenkins CI on a PR. label May 27, 2026
Copy link
Copy Markdown
Member

@ronag ronag left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This might still break stuff. Some streams override emit. @mcollina wdyt?

@codecov
Copy link
Copy Markdown

codecov Bot commented May 27, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 90.31%. Comparing base (4639dcb) to head (4708ae2).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main   #63592      +/-   ##
==========================================
+ Coverage   90.29%   90.31%   +0.01%     
==========================================
  Files         730      730              
  Lines      234695   234713      +18     
  Branches    43956    43969      +13     
==========================================
+ Hits       211927   211976      +49     
+ Misses      14494    14447      -47     
- Partials     8274     8290      +16     
Files with missing lines Coverage Δ
lib/internal/streams/readable.js 97.26% <100.00%> (+0.02%) ⬆️

... and 47 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@trivikr

This comment was marked as outdated.

@trivikr

This comment was marked as outdated.

@trivikr trivikr marked this pull request as draft May 27, 2026 17:36
@trivikr

This comment was marked as outdated.

trivikr added 3 commits May 27, 2026 18:35
Avoid EventEmitter dispatch for the readable pipe data handler when it
is the only data listener. Fall back to normal data event emission when
additional data listeners are present to preserve listener ordering.

Track the pipe data handler lazily so non-piped readable streams do not
pay an extra ReadableState property initialization or data listener
identity check.

Signed-off-by: Kamat, Trivikram <16024985+trivikr@users.noreply.github.com>
Assisted-by: openai:gpt-5.5
@trivikr trivikr force-pushed the stream-pipe-hot-path-listener-overhead branch from 3184889 to 654d196 Compare May 28, 2026 01:35
@trivikr
Copy link
Copy Markdown
Member Author

trivikr commented May 28, 2026

I attempted to make pipe data fast path state lazy in 654d196 (this PR).

New Benchmark CI: https://ci.nodejs.org/view/Node.js%20benchmark/job/benchmark-node-micro-benchmarks/1863/

Details
                                                                                         confidence improvement accuracy (*)    (**)   (***)
streams/compose.js n=1000                                                                       ***     -1.24 %       ±0.48%  ±0.64%  ±0.83%
streams/creation.js kind='duplex' n=50000000                                                      *      0.90 %       ±0.77%  ±1.03%  ±1.35%
streams/creation.js kind='readable' n=50000000                                                          -0.38 %       ±1.22%  ±1.63%  ±2.13%
streams/creation.js kind='transform' n=50000000                                                          0.76 %       ±1.47%  ±1.97%  ±2.59%
streams/creation.js kind='writable' n=50000000                                                          -0.23 %       ±0.77%  ±1.03%  ±1.34%
streams/destroy.js kind='duplex' n=1000000                                                               0.13 %       ±0.51%  ±0.67%  ±0.88%
streams/destroy.js kind='readable' n=1000000                                                             0.39 %       ±0.85%  ±1.13%  ±1.47%
streams/destroy.js kind='transform' n=1000000                                                            0.07 %       ±0.39%  ±0.52%  ±0.68%
streams/destroy.js kind='writable' n=1000000                                                            -0.08 %       ±0.63%  ±0.83%  ±1.08%
streams/finished.js streamType='readable' n=10000000                                                    -0.21 %       ±0.67%  ±0.89%  ±1.16%
streams/finished.js streamType='writable' n=10000000                                                    -0.59 %       ±1.16%  ±1.55%  ±2.04%
streams/iter-creation.js n=100000 type='pair' api='classic'                                             -0.04 %       ±4.19%  ±5.58%  ±7.28%
streams/iter-creation.js n=100000 type='pair' api='iter'                                                 0.41 %       ±0.54%  ±0.72%  ±0.94%
streams/iter-creation.js n=100000 type='pair' api='webstream'                                            0.27 %       ±0.79%  ±1.05%  ±1.36%
streams/iter-creation.js n=100000 type='readable' api='classic'                                          0.96 %       ±7.79% ±10.37% ±13.49%
streams/iter-creation.js n=100000 type='readable' api='iter'                                             0.49 %       ±2.50%  ±3.32%  ±4.32%
streams/iter-creation.js n=100000 type='readable' api='webstream'                                        0.48 %       ±0.87%  ±1.15%  ±1.50%
streams/iter-creation.js n=100000 type='transform' api='classic'                                        -1.11 %       ±2.85%  ±3.80%  ±4.94%
streams/iter-creation.js n=100000 type='transform' api='webstream'                                       0.26 %       ±0.92%  ±1.23%  ±1.60%
streams/iter-creation.js n=100000 type='writable' api='classic'                                          1.83 %       ±3.55%  ±4.73%  ±6.16%
streams/iter-creation.js n=100000 type='writable' api='iter'                                            -0.11 %       ±2.52%  ±3.36%  ±4.37%
streams/iter-creation.js n=100000 type='writable' api='webstream'                                        0.22 %       ±1.99%  ±2.64%  ±3.44%
streams/iter-file-read.js n=5 filesize=1048576 api='classic'                                             4.34 %       ±8.74% ±11.63% ±15.15%
streams/iter-file-read.js n=5 filesize=1048576 api='iter'                                                1.79 %      ±10.99% ±14.62% ±19.04%
streams/iter-file-read.js n=5 filesize=1048576 api='webstream'                                           1.91 %       ±6.05%  ±8.06% ±10.49%
streams/iter-file-read.js n=5 filesize=16777216 api='classic'                                            0.30 %       ±6.80%  ±9.05% ±11.78%
streams/iter-file-read.js n=5 filesize=16777216 api='iter'                                              -7.58 %      ±21.98% ±29.25% ±38.08%
streams/iter-file-read.js n=5 filesize=16777216 api='webstream'                                          0.27 %       ±0.48%  ±0.65%  ±0.84%
streams/iter-file-read.js n=5 filesize=67108864 api='classic'                                           -0.91 %       ±4.89%  ±6.50%  ±8.46%
streams/iter-file-read.js n=5 filesize=67108864 api='iter'                                               0.43 %      ±15.01% ±19.99% ±26.07%
streams/iter-file-read.js n=5 filesize=67108864 api='webstream'                                          0.01 %       ±0.45%  ±0.60%  ±0.78%
streams/iter-from-batching.js n=100 chunkSize=16 chunks=256 method='from-sync-writev'                    2.28 %       ±6.41%  ±8.62% ±11.41%
streams/iter-from-batching.js n=100 chunkSize=16 chunks=4096 method='from-sync-writev'                  -1.06 %       ±4.00%  ±5.32%  ±6.94%
streams/iter-from-batching.js n=1000 chunkSize=16 chunks=16384 method='from-first-batch'                 3.36 %       ±5.96%  ±7.94% ±10.35%
streams/iter-from-batching.js n=1000 chunkSize=16 chunks=256 method='from-first-batch'                   0.43 %       ±3.77%  ±5.01%  ±6.53%
streams/iter-from-batching.js n=1000 chunkSize=16 chunks=4096 method='from-first-batch'                 -2.53 %       ±5.43%  ±7.23%  ±9.41%
streams/iter-throughput-broadcast.js n=5 datasize=1048576 consumers=1 api='classic'                     -0.47 %       ±0.98%  ±1.31%  ±1.70%
streams/iter-throughput-broadcast.js n=5 datasize=1048576 consumers=1 api='iter'                         0.38 %       ±0.87%  ±1.17%  ±1.53%
streams/iter-throughput-broadcast.js n=5 datasize=1048576 consumers=1 api='webstream'             *     14.96 %      ±14.75% ±19.62% ±25.54%
streams/iter-throughput-broadcast.js n=5 datasize=1048576 consumers=2 api='classic'                     -1.40 %       ±2.66%  ±3.57%  ±4.71%
streams/iter-throughput-broadcast.js n=5 datasize=1048576 consumers=2 api='iter'                        -0.73 %       ±2.17%  ±2.91%  ±3.85%
streams/iter-throughput-broadcast.js n=5 datasize=1048576 consumers=2 api='webstream'                    1.71 %      ±13.00% ±17.30% ±22.52%
streams/iter-throughput-broadcast.js n=5 datasize=1048576 consumers=4 api='classic'               *     -3.99 %       ±3.77%  ±5.09%  ±6.75%
streams/iter-throughput-broadcast.js n=5 datasize=1048576 consumers=4 api='iter'                        18.59 %      ±22.30% ±29.74% ±38.84%
streams/iter-throughput-broadcast.js n=5 datasize=1048576 consumers=4 api='webstream'                    0.01 %       ±8.63% ±11.49% ±14.97%
streams/iter-throughput-broadcast.js n=5 datasize=16777216 consumers=1 api='classic'            ***     63.98 %       ±7.71% ±10.32% ±13.57%
streams/iter-throughput-broadcast.js n=5 datasize=16777216 consumers=1 api='iter'                        2.62 %      ±14.61% ±19.44% ±25.31%
streams/iter-throughput-broadcast.js n=5 datasize=16777216 consumers=1 api='webstream'                  -0.19 %       ±8.70% ±11.58% ±15.08%
streams/iter-throughput-broadcast.js n=5 datasize=16777216 consumers=2 api='classic'                     1.22 %       ±8.10% ±10.77% ±14.02%
streams/iter-throughput-broadcast.js n=5 datasize=16777216 consumers=2 api='iter'                        1.45 %      ±14.03% ±18.66% ±24.30%
streams/iter-throughput-broadcast.js n=5 datasize=16777216 consumers=2 api='webstream'                  -4.70 %       ±7.73% ±10.30% ±13.42%
streams/iter-throughput-broadcast.js n=5 datasize=16777216 consumers=4 api='classic'                    -0.47 %       ±7.44%  ±9.90% ±12.89%
streams/iter-throughput-broadcast.js n=5 datasize=16777216 consumers=4 api='iter'                       -0.53 %      ±11.75% ±15.63% ±20.36%
streams/iter-throughput-broadcast.js n=5 datasize=16777216 consumers=4 api='webstream'                   4.26 %       ±5.74%  ±7.63%  ±9.94%
streams/iter-throughput-compression.js n=5 datasize=1048576 api='classic'                              -11.73 %      ±12.91% ±17.18% ±22.36%
streams/iter-throughput-compression.js n=5 datasize=1048576 api='iter'                                  13.59 %      ±14.80% ±19.69% ±25.63%
streams/iter-throughput-compression.js n=5 datasize=1048576 api='webstream'                             -5.98 %      ±10.44% ±13.89% ±18.08%
streams/iter-throughput-compression.js n=5 datasize=16777216 api='classic'                             -12.01 %      ±27.15% ±36.22% ±47.35%
streams/iter-throughput-compression.js n=5 datasize=16777216 api='iter'                                  9.79 %      ±25.84% ±34.39% ±44.78%
streams/iter-throughput-compression.js n=5 datasize=16777216 api='webstream'                           -12.14 %      ±21.65% ±28.81% ±37.49%
streams/iter-throughput-compression.js n=5 datasize=67108864 api='classic'                              -2.31 %      ±19.01% ±25.31% ±32.99%
streams/iter-throughput-compression.js n=5 datasize=67108864 api='iter'                                -10.09 %      ±20.18% ±26.88% ±35.05%
streams/iter-throughput-compression.js n=5 datasize=67108864 api='webstream'                           -12.56 %      ±18.86% ±25.12% ±32.74%
streams/iter-throughput-identity.js n=5 datasize=1048576 api='classic'                                  -2.24 %       ±3.62%  ±4.87%  ±6.43%
streams/iter-throughput-identity.js n=5 datasize=1048576 api='iter-sync'                                -0.77 %       ±1.80%  ±2.40%  ±3.14%
streams/iter-throughput-identity.js n=5 datasize=1048576 api='iter'                                     -2.19 %       ±3.44%  ±4.64%  ±6.14%
streams/iter-throughput-identity.js n=5 datasize=1048576 api='webstream'                                 1.42 %      ±14.32% ±19.05% ±24.79%
streams/iter-throughput-identity.js n=5 datasize=16777216 api='classic'                                  6.81 %       ±7.90% ±10.53% ±13.75%
streams/iter-throughput-identity.js n=5 datasize=16777216 api='iter-sync'                               17.72 %      ±38.16% ±50.79% ±66.14%
streams/iter-throughput-identity.js n=5 datasize=16777216 api='iter'                                   -12.37 %      ±15.90% ±21.19% ±27.66%
streams/iter-throughput-identity.js n=5 datasize=16777216 api='webstream'                               -1.38 %       ±9.38% ±12.48% ±16.25%
streams/iter-throughput-identity.js n=5 datasize=67108864 api='classic'                                  6.50 %       ±8.21% ±10.92% ±14.22%
streams/iter-throughput-identity.js n=5 datasize=67108864 api='iter-sync'                                0.64 %      ±10.75% ±14.31% ±18.64%
streams/iter-throughput-identity.js n=5 datasize=67108864 api='iter'                                     5.40 %      ±11.81% ±15.74% ±20.55%
streams/iter-throughput-identity.js n=5 datasize=67108864 api='webstream'                               -1.88 %       ±5.68%  ±7.56%  ±9.85%
streams/iter-throughput-pipeto.js n=5 datasize=1048576 api='classic'                            ***     -5.22 %       ±0.66%  ±0.88%  ±1.14%
streams/iter-throughput-pipeto.js n=5 datasize=1048576 api='iter-sync-source'                           -0.81 %       ±0.95%  ±1.26%  ±1.65%
streams/iter-throughput-pipeto.js n=5 datasize=1048576 api='iter-sync'                                   2.25 %       ±2.45%  ±3.27%  ±4.29%
streams/iter-throughput-pipeto.js n=5 datasize=1048576 api='iter'                                       -0.34 %       ±1.13%  ±1.50%  ±1.95%
streams/iter-throughput-pipeto.js n=5 datasize=1048576 api='webstream'                                  12.42 %      ±14.18% ±18.87% ±24.56%
streams/iter-throughput-pipeto.js n=5 datasize=16777216 api='classic'                           ***     52.73 %      ±10.13% ±13.48% ±17.55%
streams/iter-throughput-pipeto.js n=5 datasize=16777216 api='iter-sync-source'                          -6.83 %      ±22.09% ±29.40% ±38.30%
streams/iter-throughput-pipeto.js n=5 datasize=16777216 api='iter-sync'                                 26.44 %      ±39.86% ±53.07% ±69.15%
streams/iter-throughput-pipeto.js n=5 datasize=16777216 api='iter'                                      -9.74 %      ±13.16% ±17.55% ±22.91%
streams/iter-throughput-pipeto.js n=5 datasize=16777216 api='webstream'                                 -5.65 %       ±8.70% ±11.61% ±15.17%
streams/iter-throughput-pipeto.js n=5 datasize=67108864 api='classic'                                    4.16 %       ±8.54% ±11.37% ±14.84%
streams/iter-throughput-pipeto.js n=5 datasize=67108864 api='iter-sync-source'                          -3.07 %       ±7.92% ±10.66% ±14.11%
streams/iter-throughput-pipeto.js n=5 datasize=67108864 api='iter-sync'                                  3.56 %       ±8.59% ±11.57% ±15.33%
streams/iter-throughput-pipeto.js n=5 datasize=67108864 api='iter'                                       3.85 %       ±5.40%  ±7.23%  ±9.51%
streams/iter-throughput-pipeto.js n=5 datasize=67108864 api='webstream'                                  0.55 %       ±6.56%  ±8.73% ±11.37%
streams/iter-throughput-share.js n=5 backpressure='block' batches=10000 consumers=2                      0.59 %       ±1.60%  ±2.14%  ±2.78%
streams/iter-throughput-share.js n=5 backpressure='block' batches=10000 consumers=32                    -0.20 %       ±0.38%  ±0.50%  ±0.65%
streams/iter-throughput-share.js n=5 backpressure='block' batches=10000 consumers=8              **     -1.13 %       ±0.70%  ±0.93%  ±1.21%
streams/iter-throughput-transform.js n=5 datasize=1048576 api='classic'                         ***    -21.36 %       ±7.91% ±10.61% ±13.98%
streams/iter-throughput-transform.js n=5 datasize=1048576 api='iter-sync'                               -0.56 %       ±1.28%  ±1.71%  ±2.22%
streams/iter-throughput-transform.js n=5 datasize=1048576 api='iter'                                     0.69 %      ±16.89% ±22.47% ±29.25%
streams/iter-throughput-transform.js n=5 datasize=1048576 api='webstream'                                1.45 %       ±8.26% ±10.99% ±14.31%
streams/iter-throughput-transform.js n=5 datasize=16777216 api='classic'                        ***      7.88 %       ±3.76%  ±5.01%  ±6.54%
streams/iter-throughput-transform.js n=5 datasize=16777216 api='iter-sync'                              -1.54 %       ±6.14%  ±8.17% ±10.63%
streams/iter-throughput-transform.js n=5 datasize=16777216 api='iter'                                    2.59 %       ±8.63% ±11.48% ±14.96%
streams/iter-throughput-transform.js n=5 datasize=16777216 api='webstream'                              -4.19 %       ±5.14%  ±6.83%  ±8.90%
streams/iter-throughput-transform.js n=5 datasize=67108864 api='classic'                                -3.05 %       ±4.07%  ±5.44%  ±7.12%
streams/iter-throughput-transform.js n=5 datasize=67108864 api='iter-sync'                               0.23 %       ±3.17%  ±4.22%  ±5.50%
streams/iter-throughput-transform.js n=5 datasize=67108864 api='iter'                                    2.28 %       ±3.98%  ±5.30%  ±6.90%
streams/iter-throughput-transform.js n=5 datasize=67108864 api='webstream'                               0.01 %       ±4.91%  ±6.54%  ±8.51%
streams/pipe-object-mode.js n=5000000                                                           ***     23.77 %       ±1.31%  ±1.75%  ±2.31%
streams/pipe.js n=5000000                                                                       ***     12.56 %       ±0.70%  ±0.94%  ±1.23%
streams/readable-async-iterator.js sync='no' n=100000                                                   -0.48 %       ±1.75%  ±2.33%  ±3.03%
streams/readable-async-iterator.js sync='yes' n=100000                                                  -2.63 %       ±4.67%  ±6.23%  ±8.16%
streams/readable-bigread.js n=1000                                                                       1.32 %       ±1.85%  ±2.46%  ±3.20%
streams/readable-bigunevenread.js n=1000                                                                -0.25 %       ±0.80%  ±1.07%  ±1.39%
streams/readable-boundaryread.js type='buffer' n=2000                                             *     -0.66 %       ±0.51%  ±0.68%  ±0.89%
streams/readable-boundaryread.js type='string' n=2000                                                   -0.97 %       ±1.16%  ±1.54%  ±2.01%
streams/readable-from.js type='array' n=10000000                                                         1.38 %       ±2.20%  ±2.93%  ±3.81%
streams/readable-from.js type='async-generator' n=10000000                                      ***     -6.56 %       ±1.22%  ±1.63%  ±2.12%
streams/readable-from.js type='sync-generator-with-async-values' n=10000000                     ***     -7.26 %       ±1.89%  ±2.53%  ±3.33%
streams/readable-from.js type='sync-generator-with-sync-values' n=10000000                              -0.24 %       ±0.86%  ±1.15%  ±1.49%
streams/readable-readall.js n=5000                                                                      -0.08 %       ±1.08%  ±1.44%  ±1.87%
streams/readable-uint8array.js kind='encoding' n=1000000                                                -0.11 %       ±1.04%  ±1.39%  ±1.81%
streams/readable-uint8array.js kind='read' n=1000000                                                     1.09 %       ±1.19%  ±1.58%  ±2.06%
streams/readable-unevenread.js n=1000                                                                   -0.59 %       ±1.07%  ±1.42%  ±1.85%
streams/writable-manywrites.js len=1024 callback='no' writev='no' sync='no' n=100000                    -1.30 %       ±3.81%  ±5.07%  ±6.61%
streams/writable-manywrites.js len=1024 callback='no' writev='no' sync='yes' n=100000                    3.88 %      ±11.98% ±15.95% ±20.77%
streams/writable-manywrites.js len=1024 callback='no' writev='yes' sync='no' n=100000                   -0.30 %       ±5.66%  ±7.53%  ±9.80%
streams/writable-manywrites.js len=1024 callback='no' writev='yes' sync='yes' n=100000                   1.94 %       ±5.24%  ±7.02%  ±9.24%
streams/writable-manywrites.js len=1024 callback='yes' writev='no' sync='no' n=100000                    2.75 %       ±3.00%  ±4.00%  ±5.22%
streams/writable-manywrites.js len=1024 callback='yes' writev='no' sync='yes' n=100000            *     -8.60 %       ±7.30%  ±9.72% ±12.66%
streams/writable-manywrites.js len=1024 callback='yes' writev='yes' sync='no' n=100000                   4.25 %       ±4.72%  ±6.28%  ±8.17%
streams/writable-manywrites.js len=1024 callback='yes' writev='yes' sync='yes' n=100000                  0.67 %       ±2.74%  ±3.64%  ±4.75%
streams/writable-manywrites.js len=32768 callback='no' writev='no' sync='no' n=100000                   -2.89 %       ±4.01%  ±5.33%  ±6.94%
streams/writable-manywrites.js len=32768 callback='no' writev='no' sync='yes' n=100000                   4.22 %      ±10.35% ±13.78% ±17.94%
streams/writable-manywrites.js len=32768 callback='no' writev='yes' sync='no' n=100000                   0.23 %       ±4.66%  ±6.20%  ±8.08%
streams/writable-manywrites.js len=32768 callback='no' writev='yes' sync='yes' n=100000                 -1.09 %       ±3.33%  ±4.44%  ±5.78%
streams/writable-manywrites.js len=32768 callback='yes' writev='no' sync='no' n=100000                   1.22 %       ±3.29%  ±4.38%  ±5.70%
streams/writable-manywrites.js len=32768 callback='yes' writev='no' sync='yes' n=100000                  4.61 %       ±8.41% ±11.20% ±14.58%
streams/writable-manywrites.js len=32768 callback='yes' writev='yes' sync='no' n=100000                  2.25 %       ±4.84%  ±6.44%  ±8.39%
streams/writable-manywrites.js len=32768 callback='yes' writev='yes' sync='yes' n=100000                 0.19 %       ±1.76%  ±2.34%  ±3.05%
streams/writable-uint8array.js kind='object-mode' n=50000000                                            -0.04 %       ±0.12%  ±0.17%  ±0.22%
streams/writable-uint8array.js kind='write' n=50000000                                                   0.02 %       ±0.61%  ±0.81%  ±1.07%
streams/writable-uint8array.js kind='writev' n=50000000                                                  0.04 %       ±0.24%  ±0.32%  ±0.42%

@trivikr trivikr marked this pull request as ready for review May 28, 2026 02:41
@trivikr

This comment was marked as outdated.

@trivikr
Copy link
Copy Markdown
Member Author

trivikr commented May 28, 2026

After inlining the non-pipe data emission path in 4708ae2 (this PR), the previous readable-from regressions are no longer present.

The Readable.from(...).on('data') cases are now within noise:

  • async-generator: +0.50%
  • sync-generator-with-async-values: -0.58%
  • sync-generator-with-sync-values: -0.12%
  • array: +0.45%

The targeted pipe wins are preserved:

  • streams/pipe.js: +12.45%
  • streams/pipe-object-mode.js: +25.80%

Large classic iterator pipeline cases also still improve significantly:

  • iter-throughput-broadcast 16MiB, 1 consumer, classic: +63.39%
  • iter-throughput-pipeto 16MiB, classic: +50.39%

The remaining notable regression is iter-throughput-transform 1MiB classic at -22.87%, which appears separate from the earlier readable-from regression.

Benchmark CI: https://ci.nodejs.org/view/Node.js%20benchmark/job/benchmark-node-micro-benchmarks/1864/


Outside of these benchmarks, there's an open question if this change will break things in #63592 (review)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs-ci PRs that need a full CI run. request-ci Add this label to start a Jenkins CI on a PR. stream Issues and PRs related to the stream subsystem.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants