Skip to content

Conversation

yakryder
Copy link

Ref #6504

Reduces lock contention in timer operations by registering timers in a per-worker HashMap for the multi-threaded runtime, while falling back to the global timer wheel for current_thread runtime and block_in_place contexts.

Benchmark

Added benches/time_drop_sleep_contention.rs to measure timer drop performance:

  • Single-threaded: 33.3ms → 32.7ms (no regression)
  • Multi-threaded (8 workers): 21.6ms → 16.0ms (25.9% faster)

This benchmark demonstrates the mutex contention issue described
in tokio-rs#6504, specifically focusing on the drop path for timers that
are registered but never fire.

The benchmark creates 10,000 sleep timers, polls each once to
initialize and register it with the timer wheel, then drops them
before they fire. This simulates the common case of timeouts
that don't fire (e.g., operations that complete before their
timeout).

Baseline results show severe contention: the 8-worker case is
only ~1.5x faster than single-threaded.

Refs: tokio-rs#6504
Reduces lock contention in timer operations by registering timers in a
per-worker HashMap for the multi-threaded runtime, while falling back
to the global timer wheel for current_thread runtime and block_in_place.

Benchmark results (benches/time_drop_sleep_contention.rs):
- Single-threaded: 33.3ms → 32.7ms (no regression)
- Multi-threaded (8 workers): 21.6ms → 16.0ms (25.9% faster)

Refs tokio-rs#6504
@github-actions github-actions bot added R-loom-multi-thread Run loom multi-thread tests on this PR R-loom-time-driver Run loom time driver tests on this PR labels Oct 10, 2025
Comment on lines +1075 to +1081
fn fire_expired_timers(&mut self, now: Instant) {
self.timers.retain(|&deadline, wakers| {
(now < deadline) || {
wakers.drain(..).for_each(Waker::wake);
false
}
});
Copy link
Contributor

Choose a reason for hiding this comment

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

This is a very expensive operation.

/// This is called from TimerEntry::poll_elapsed when a timer is registered.
/// The waker will be fired when fire_expired_timers() is called with a time
/// >= deadline.
pub(crate) fn register_timer(&mut self, deadline: Instant, waker: Waker) {
Copy link
Member

Choose a reason for hiding this comment

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

Apart from issues of O(n) time complexity, how to de-register a timer?

@Darksonn Darksonn added A-tokio Area: The main tokio crate M-time Module: tokio/time labels Oct 11, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-tokio Area: The main tokio crate M-time Module: tokio/time R-loom-multi-thread Run loom multi-thread tests on this PR R-loom-time-driver Run loom time driver tests on this PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants