Skip to content

Conversation

@pwojcikdev
Copy link
Contributor

This reworks thread_pool class to reduce condition variable wake up overhead during heavy tasks processing. Preserves the current implementation as timed_thread_pool.

@gr0vity-dev-bot
Copy link

gr0vity-dev-bot commented Sep 23, 2025

Test Results for Commit e646703

Pull Request 4950: Results
Overall Status:

Test Case Results

  • 5n4pr_conf_10k_bintree: PASS (Duration: 130s)
  • 5n4pr_conf_10k_change: PASS (Duration: 197s)
  • 5n4pr_conf_change_dependant: PASS (Duration: 137s)
  • 5n4pr_conf_change_independant: PASS (Duration: 147s)
  • 5n4pr_conf_send_dependant: PASS (Duration: 134s)
  • 5n4pr_conf_send_independant: PASS (Duration: 127s)
  • 5n4pr_rocks_10k_bintree: PASS (Duration: 121s)
  • 5n4pr_rocks_10k_change: FAIL (Duration: 274s)
  • Log

Last updated: 2025-09-24 15:11:02 UTC

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR reworks the thread_pool class to implement a high-performance version that avoids condition variable overhead under heavy task processing loads, while preserving the original implementation as timed_thread_pool.

  • Introduced a new optimized thread_pool class with spinning workers and batch processing
  • Renamed the existing thread pool implementation to timed_thread_pool
  • Updated node components to use timed_thread_pool where time-sensitive operations are needed
  • Removed bootstrap_workers references from the node class

Reviewed Changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
nano/lib/thread_pool.hpp Implements new optimized thread_pool class and renames existing to timed_thread_pool
nano/node/pruning.hpp Updates pruning to use timed_thread_pool instead of thread_pool
nano/node/node.hpp Updates node worker references to use timed_thread_pool and removes bootstrap_workers
nano/node/node.cpp Updates constructor and cleanup code to use timed_thread_pool and removes bootstrap_workers
nano/core_test/thread_pool.cpp Adds comprehensive tests for new thread_pool and updates existing tests for timed_thread_pool

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

~thread_pool ()
~timed_thread_pool ()
{
// Must be stopped before destruction to avoid running takss when node components are being destroyed
Copy link

Copilot AI Sep 24, 2025

Choose a reason for hiding this comment

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

There's a typo in the comment: 'takss' should be 'tasks'.

Suggested change
// Must be stopped before destruction to avoid running takss when node components are being destroyed
// Must be stopped before destruction to avoid running tasks when node components are being destroyed

Copilot uses AI. Check for mistakes.
template <typename F>
void post (F && task)
{
if (stopped)
Copy link

Copilot AI Sep 24, 2025

Choose a reason for hiding this comment

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

Reading stopped without synchronization could lead to race conditions. This should be stopped.load() for proper atomic access.

Suggested change
if (stopped)
if (stopped.load())

Copilot uses AI. Check for mistakes.
{
// Spinning phase before blocking
bool found_work = false;
for (int spin = 0; spin < 64; ++spin) // Moderate spin count
Copy link

Copilot AI Sep 24, 2025

Choose a reason for hiding this comment

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

The magic number 64 for spin count should be defined as a named constant to improve maintainability and allow for easier tuning.

Copilot uses AI. Check for mistakes.
auto sleeping = sleeping_workers.load ();

// Only notify if we have sleeping workers and queue isn't too large
if (sleeping > 0 && queue_size <= 4)
Copy link

Copilot AI Sep 24, 2025

Choose a reason for hiding this comment

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

The magic number 4 for queue size threshold should be defined as a named constant to improve maintainability and make the heuristic more explicit.

Copilot uses AI. Check for mistakes.
@pwojcikdev pwojcikdev force-pushed the optimize-thread-pool branch 3 times, most recently from 26c9087 to 5e6c6aa Compare October 12, 2025 22:05
@pwojcikdev pwojcikdev force-pushed the optimize-thread-pool branch from 5e6c6aa to ee9da7f Compare November 6, 2025 19:39
@pwojcikdev pwojcikdev requested a review from Copilot November 6, 2025 19:39
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

Copilot reviewed 6 out of 6 changed files in this pull request and generated 4 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +135 to +136
if (stopped.load (std::memory_order_acquire))
return;
Copy link

Copilot AI Nov 6, 2025

Choose a reason for hiding this comment

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

Early return at line 136 without decrementing sleeping_workers counter could lead to a permanent counter drift. If the worker thread exits during the spinning phase after sleeping_workers has been incremented at line 156, the counter will remain incremented forever. Move the check before line 156 or ensure sleeping_workers is decremented before returning.

Copilot uses AI. Check for mistakes.
Comment on lines +156 to +162
sleeping_workers.fetch_add (1, std::memory_order_acq_rel);
{
std::unique_lock lock{ mutex };

condition.wait_for (lock, wakeup_interval, [this] {
return !tasks.empty () || stopped.load (std::memory_order_acquire);
});
Copy link

Copilot AI Nov 6, 2025

Choose a reason for hiding this comment

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

Race condition: sleeping_workers is incremented at line 156 before acquiring the mutex at line 158. This creates a window where a posting thread could read sleeping_workers > 0, decide to notify, but the worker hasn't entered wait_for yet, causing a missed wakeup. The increment should happen inside the locked section before the wait.

Copilot uses AI. Check for mistakes.
bool should_notify = false;
{
std::lock_guard lock{ mutex };

Copy link

Copilot AI Nov 6, 2025

Choose a reason for hiding this comment

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

Unlike timed_thread_pool::post() which checks if stopped before accepting tasks (line 236), thread_pool::post() does not check the stopped flag and will accept tasks even after stop() is called. This inconsistency could lead to tasks being queued but never executed. Consider adding a stopped check similar to timed_thread_pool.

Suggested change
if (stopped.load (std::memory_order_acquire))
{
// Do not accept new tasks if stopped
return;
}

Copilot uses AI. Check for mistakes.
Comment on lines +26 to 29
/*
* High-performance thread pool implementation that avoids condition variable overhead under high load
*/
class thread_pool final
Copy link

Copilot AI Nov 6, 2025

Choose a reason for hiding this comment

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

[nitpick] The class documentation should clarify when to use thread_pool versus timed_thread_pool. Based on the implementation, thread_pool is optimized for high-frequency immediate tasks with spin-locking, while timed_thread_pool supports delayed execution. Adding guidance on the trade-offs (e.g., CPU usage from spinning vs. lower latency) would help API consumers choose the right implementation.

Copilot uses AI. Check for mistakes.
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