Skip to content

perf: make no-options add 141% faster#245

Closed
saripovdenis wants to merge 1 commit into
sindresorhus:mainfrom
saripovdenis:perf/add_fast_path
Closed

perf: make no-options add 141% faster#245
saripovdenis wants to merge 1 commit into
sindresorhus:mainfrom
saripovdenis:perf/add_fast_path

Conversation

@saripovdenis
Copy link
Copy Markdown
Contributor

Summary

Optimizes the common queue.add(fn) path when no options are passed.

This avoids the default {} parameter, object spread, repeated option reads, and per-task noop cleanup closure for the no-options path while preserving behavior for timeout, priority, abort, and explicit options.

Benchmarks

Node.js v20.19.5

Benchmark Previous implementation Current implementation Change
add-no-options-paused 95.75 ops/sec ±4.78% 231 ops/sec ±3.23% +141.3%

Validation

Existing behavior tests cover no-options .add(), options copying, priority, timeout, abort signals, listener cleanup, and runningTasks.

@sindresorhus
Copy link
Copy Markdown
Owner

Thanks, but I don’t think this is the right tradeoff.

The current implementation has a simple invariant: normalize options once, then use that normalized object throughout add(). This PR makes the central lifecycle path harder to audit by adding a special no-options branch and separate captured values for id, signal, timeout, and priority.

The benchmark is also an isolated enqueue-only case on a paused queue. That makes option normalization overhead look more important than it likely is in real p-queue usage, where the queued async work, timers, abort handling, and scheduling behavior dominate.


https://sindresorhus.com/blog/micro-benchmark-fallacy

@saripovdenis
Copy link
Copy Markdown
Contributor Author

Thanks, but I don’t think this is the right tradeoff.

The current implementation has a simple invariant: normalize options once, then use that normalized object throughout add(). This PR makes the central lifecycle path harder to audit by adding a special no-options branch and separate captured values for id, signal, timeout, and priority.

The benchmark is also an isolated enqueue-only case on a paused queue. That makes option normalization overhead look more important than it likely is in real p-queue usage, where the queued async work, timers, abort handling, and scheduling behavior dominate.

https://sindresorhus.com/blog/micro-benchmark-fallacy

Great post! Thank you for explanation and your time

@saripovdenis saripovdenis deleted the perf/add_fast_path branch April 26, 2026 14:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants