Skip to content

Conversation

gurgunday
Copy link
Member

@gurgunday gurgunday commented Sep 27, 2025

Benchmarks:

main:

node % ./node benchmark/run.js --filter fixed-queue util
util/fixed-queue-oscillate.js
util/fixed-queue-oscillate.js cycles=5000: 240,760,722.06068513
util/fixed-queue.js
util/fixed-queue.js n=100000: 30,605,608.47775355

branch:

node % ./node benchmark/run.js --filter fixed-queue util
util/fixed-queue-oscillate.js
util/fixed-queue-oscillate.js cycles=5000: 283,339,287.4243814
util/fixed-queue.js
util/fixed-queue.js n=100000: 32,606,927.0808029

When the head segment fills, we reuse a “spare” FixedCircularBuffer if available; when a segment becomes empty, we reset it and stash it as the spare instead of wasting it. This cuts allocations/GC especially when the queue oscillates between N and N+1 segments, which is more than likely to be the case in real life scenarios where Events/Promises/Tasks are emitted/created and consumed in bursts.

We only call reset() when reusing a spare (not for a freshly allocated buffer).

And finally, there is no behavior change: we have the same data structure (linked fixed-size circular buffers), same invariants (one-slot-wasted), same FIFO semantics and API. Capacity per segment stays kSize - 1, so overall capacity is unchanged.

@nodejs-github-bot
Copy link
Collaborator

Review requested:

  • @nodejs/performance

@nodejs-github-bot nodejs-github-bot added the needs-ci PRs that need a full CI run. label Sep 27, 2025
Copy link

codecov bot commented Sep 27, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 88.48%. Comparing base (c6316f9) to head (0a70daa).

Additional details and impacted files
@@            Coverage Diff             @@
##             main   #60031      +/-   ##
==========================================
+ Coverage   88.45%   88.48%   +0.02%     
==========================================
  Files         703      703              
  Lines      207546   207574      +28     
  Branches    40011    40007       -4     
==========================================
+ Hits       183591   183666      +75     
+ Misses      15949    15905      -44     
+ Partials     8006     8003       -3     
Files with missing lines Coverage Δ
lib/internal/fixed_queue.js 100.00% <100.00%> (ø)

... and 32 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.

Copy link
Member

@mcollina mcollina left a comment

Choose a reason for hiding this comment

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

Great work! There are diagrams explaining this data structure at the top of the file that would need updating.

@gurgunday
Copy link
Member Author

gurgunday commented Sep 27, 2025

Great work! There are diagrams explaining this data structure at the top of the file that would need updating.

The new spare segment isn’t part of the list. It’s just a detached buffer we park for reuse once it's created, so it doesn’t really affect the diagrams. But nonetheless, I added a small paragraph to explain how it works.

Let me know if you'd like to see more explanation in the code!

@gurgunday

This comment was marked as outdated.

@gurgunday

This comment was marked as outdated.

@lpinca lpinca added the request-ci Add this label to start a Jenkins CI on a PR. label Sep 28, 2025
@github-actions github-actions bot added request-ci-failed An error occurred while starting CI via request-ci label, and manual interventon is needed. and removed request-ci Add this label to start a Jenkins CI on a PR. labels Sep 28, 2025
Copy link
Contributor

Failed to start CI
   ⚠  No approving reviews found
   ✘  Refusing to run CI on potentially unsafe PR
https://github.com/nodejs/node/actions/runs/18073218517

@gurgunday
Copy link
Member Author

@mcollina PTAL

@RafaelGSS RafaelGSS added the performance Issues and PRs related to the performance of Node.js. label Sep 29, 2025
@@ -0,0 +1,28 @@
'use strict';
Copy link
Member

Choose a reason for hiding this comment

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

I wouldn't include this inside benchmark/util. I believe benchmark/internal would be more appropriate. To be fair, creating a benchmark for a public API that makes use of FixedQueue behind the scenes would be better.

Copy link
Member Author

Choose a reason for hiding this comment

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

Hey @RafaelGSS, I looked at the other benchmarks and unfortunately I would need to create something super targeted for this. And even then, it might be hard to see the difference indirectly

This is a specific improvement that only gets triggered if we reach segment N + 1, and cycle between N and N + 1

For the benchmark, I was inspired by the priority queue benchmark. If you prefer, I can remove benchmark/util/fixed-queue-oscillate.js

Copy link
Member

@lemire lemire left a comment

Choose a reason for hiding this comment

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

It is a tradeoff: using more memory but also getting more performance.

@gurgunday
Copy link
Member Author

It is a tradeoff: using more memory but also getting more performance.

Yes, that's correct. It hinges on the idea that if we reach N+1 segments, there is a higher chance we will reach there again. But the good thing here is that we keep at max 1 extra spare segment. If 2 segments are empty (we're at N-1), segment N+1 will still be GC'd with its spare

@lemire
Copy link
Member

lemire commented Sep 29, 2025

@gurgunday Yes, to be clearer, my comment was tied with my approval of the PR.

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. performance Issues and PRs related to the performance of Node.js. request-ci-failed An error occurred while starting CI via request-ci label, and manual interventon is needed.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants