Skip to content

Updated QueueCore#ensure_workers to reduce the runtime complexity - #88

Open
gaojieliu wants to merge 2 commits into
tikv:masterfrom
gaojieliu:goliu/reduce_unpark_complexity
Open

Updated QueueCore#ensure_workers to reduce the runtime complexity#88
gaojieliu wants to merge 2 commits into
tikv:masterfrom
gaojieliu:goliu/reduce_unpark_complexity

Conversation

@gaojieliu

@gaojieliu gaojieliu commented Feb 14, 2026

Copy link
Copy Markdown

This function is calling parking_lot_core::unpark_filter to unpark sleeping threads and every time, it would only unpark one thread, but the original code will iterate all the parked threads regardless. Here is the original discussion:
https://github.com/tikv/yatp/pull/65/changes#r809711564 The reason is that we need to take care of the scenario, where the core_thread_count is smaller than max_thread_count, so the PR owner chooses to use FilterOp::Skip to scan all the parked threads all the time, which is very inefficient.
To handle all the scenarios, this PR will check whether it has already unparked one thread or not, if yes, return FilterOp::Stop, otherwise, return FilterOp::Skip. This will reduce the runtime complexity from O(n) [n: parked thread] to O(1) when core_thread_count is equal to max_thread_count.
This is important since this logic is in hot path (push) and the whole unpark logic is guarded by a global lock per queue.

Summary by CodeRabbit

  • Chores
    • Wake-up scanning in the thread pool now stops early once sufficient threads have been woken, reducing unnecessary scanning and CPU overhead during wake-up cycles.
  • New Features
    • Local work queues now expose a public numeric identifier.
  • Tests
    • Added a test validating wake/unpark behavior across multiple ensure-worker invocations and orderly thread wakeups.

@ti-chi-bot

ti-chi-bot Bot commented Feb 14, 2026

Copy link
Copy Markdown

Welcome @gaojieliu! It looks like this is your first PR to tikv/yatp 🎉

@coderabbitai

coderabbitai Bot commented Feb 14, 2026

Copy link
Copy Markdown

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Short-circuits the unpark loop in ensure_workers to stop scanning after a thread is unparked, exposes pub fn id(&self) -> usize on Local<T>, and adds a test that verifies parking/unparking behavior across two ensure_workers calls.

Changes

Cohort / File(s) Summary
Worker Unparking & Local API
src/pool/spawn.rs
ensure_workers now returns FilterOp::Stop after an unpark to short-circuit further scanning; added pub fn id(&self) -> usize on Local<T>.
Tests for Unparking Behavior
src/pool/tests.rs
Add test_ensure_workers_unparks_only_core_threads_across_two_calls with imports (QueueType, callback, AtomicUsize, Ordering, Arc) to exercise controlled parking/unparking across two ensure_workers invocations.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐇 I nudged a sleepy thread at dawn,

one wake, one skip, then marching on,
the loop now halts when one eye peeps,
queues breathe light where silence sleeps,
carrots clapped — the garden keeps.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: updating QueueCore#ensure_workers to reduce runtime complexity by short-circuiting the unpark_filter loop.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/pool/tests.rs (1)

382-396: Test relies on FIFO ordering of parking_lot_core — document the assumption.

The assertions on the unpark order (thread 3 first, then skip 6, then thread 2) depend on parking_lot_core::unpark_filter visiting waiters in FIFO order. This is currently true but is an implementation detail. Consider adding a brief comment near the top of the test noting this dependency so future maintainers know the test may break if the parking lot changes its queuing discipline.

Also applies to: 404-418

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/pool/tests.rs` around lines 382 - 396, The test for core.ensure_workers
uses the observed unpark order (checking unparked_ids contents and expecting id
3 then others) which relies on parking_lot_core::unpark_filter visiting waiters
in FIFO order; add a short comment above this test (near the start of the test
file) documenting that the assertions depend on parking_lot_core's FIFO queuing
behavior and that this is an implementation detail that may change, referencing
ensure_workers and unparked_ids so future maintainers know the dependency and
can adjust the test if parking_lot_core changes its ordering.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/pool/tests.rs`:
- Around line 349-367: Compute the expected parked count before spawning the
thread to avoid the race where the spawned thread increments parked_count before
the parent computes expected; specifically, move the calculation expected =
parked_count.load(Ordering::SeqCst) + 1 to just before calling thread::spawn (so
it is done prior to the spawned thread running), keep the spawned closure that
calls parked_count.fetch_add(1, Ordering::SeqCst), and then after spawn use the
existing while loop waiting for parked_count to reach expected and the short
sleep to ensure the thread entered local.pop_or_sleep and recorded into
unparked_ids.

---

Nitpick comments:
In `@src/pool/tests.rs`:
- Around line 382-396: The test for core.ensure_workers uses the observed unpark
order (checking unparked_ids contents and expecting id 3 then others) which
relies on parking_lot_core::unpark_filter visiting waiters in FIFO order; add a
short comment above this test (near the start of the test file) documenting that
the assertions depend on parking_lot_core's FIFO queuing behavior and that this
is an implementation detail that may change, referencing ensure_workers and
unparked_ids so future maintainers know the dependency and can adjust the test
if parking_lot_core changes its ordering.

Comment thread src/pool/tests.rs
@gaojieliu

Copy link
Copy Markdown
Author

Close it for now.

@gaojieliu gaojieliu closed this Feb 18, 2026
@gaojieliu gaojieliu reopened this Mar 4, 2026
@gaojieliu
gaojieliu force-pushed the goliu/reduce_unpark_complexity branch from 4014e8a to e3ceb31 Compare March 4, 2026 18:33
@gaojieliu
gaojieliu force-pushed the goliu/reduce_unpark_complexity branch from e3ceb31 to 39c2d92 Compare March 4, 2026 18:35
This function is calling parking_lot_core::unpark_filter to unpark sleeping threads and every time,
it would only unpark one thread, but the original code will iterate all the parked threads regardless.
Here is the original discussion:
https://github.com/tikv/yatp/pull/65/changes#r809711564
The reason is that we need to take care of the scenario, where the core_thread_count is smaller than
max_thread_count, so the PR owner chooses to use FilterOp::Skip to scan all the parked threads all the time,
which is very inefficient.
To handle all the scenarios, this PR will check whether it has already unparked one thread or not,
if yes, return FilterOp::Stop, otherwise, return FilterOp::Skip.
This will reduce the runtime complexity from O(n) [n: parked thread] to O(1) when core_thread_count
is equal to max_thread_count.
This is important since this logic is in hot path (push) and the whole unpark logic is guarded
by a global lock per queue.

Signed-off-by: Gaojie Liu <goliu@linkedin.com>
@gaojieliu
gaojieliu force-pushed the goliu/reduce_unpark_complexity branch from 39c2d92 to fac1cca Compare March 4, 2026 18:37
@gaojieliu

Copy link
Copy Markdown
Author

@BusyJay @ethercflow @sticnarf
Since you folks were involved in the original conversion in this comment:
https://github.com/tikv/yatp/pull/65/changes#r809711564
Can you take a look at this change to see whether it makes sense or not?

@gaojieliu

Copy link
Copy Markdown
Author

@ethercflow
Can you share how I can run the benchmark against this change?
I was referring to this benchmark mentioned in another PR:
#65 (comment)

Move parked_count expected computation before thread::spawn to prevent
a race where the spawned thread increments the counter before the parent
reads it, which could cause the wait loop to spin forever.

Also add a comment noting the test depends on parking_lot_core's FIFO
waiter ordering, which is an implementation detail.

Signed-off-by: Gaojie Liu <goliu@linkedin.com>
@gaojieliu
gaojieliu force-pushed the goliu/reduce_unpark_complexity branch from 402a1da to 8c2a5e7 Compare March 6, 2026 21:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant