-
-
Notifications
You must be signed in to change notification settings - Fork 2.8k
time: eliminate timer wheel allocations #6779
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 2 commits
302a757
68e5cf1
e9c4351
1610c12
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -28,6 +28,7 @@ use crate::util::WakeList; | |||||
|
|
||||||
| use crate::loom::sync::atomic::AtomicU64; | ||||||
| use std::fmt; | ||||||
| use std::sync::RwLock; | ||||||
| use std::{num::NonZeroU64, ptr::NonNull}; | ||||||
|
|
||||||
| struct AtomicOptionNonZeroU64(AtomicU64); | ||||||
|
|
@@ -115,7 +116,7 @@ struct Inner { | |||||
| next_wake: AtomicOptionNonZeroU64, | ||||||
|
|
||||||
| /// Sharded Timer wheels. | ||||||
| wheels: Box<[Mutex<wheel::Wheel>]>, | ||||||
| wheels: RwLock<ShardedWheel>, | ||||||
|
|
||||||
| /// True if the driver is being shutdown. | ||||||
| pub(super) is_shutdown: AtomicBool, | ||||||
|
|
@@ -130,6 +131,9 @@ struct Inner { | |||||
| did_wake: AtomicBool, | ||||||
| } | ||||||
|
|
||||||
| /// Wrapper around the sharded timer wheels. | ||||||
| struct ShardedWheel(Box<[Mutex<wheel::Wheel>]>); | ||||||
|
|
||||||
| // ===== impl Driver ===== | ||||||
|
|
||||||
| impl Driver { | ||||||
|
|
@@ -149,7 +153,7 @@ impl Driver { | |||||
| time_source, | ||||||
| inner: Inner { | ||||||
| next_wake: AtomicOptionNonZeroU64::new(None), | ||||||
| wheels: wheels.into_boxed_slice(), | ||||||
| wheels: RwLock::new(ShardedWheel(wheels.into_boxed_slice())), | ||||||
| is_shutdown: AtomicBool::new(false), | ||||||
| #[cfg(feature = "test-util")] | ||||||
| did_wake: AtomicBool::new(false), | ||||||
|
|
@@ -190,23 +194,28 @@ impl Driver { | |||||
| assert!(!handle.is_shutdown()); | ||||||
|
|
||||||
| // Finds out the min expiration time to park. | ||||||
| let locks = (0..rt_handle.time().inner.get_shard_size()) | ||||||
| .map(|id| rt_handle.time().inner.lock_sharded_wheel(id)) | ||||||
| .collect::<Vec<_>>(); | ||||||
|
|
||||||
| let expiration_time = locks | ||||||
| .iter() | ||||||
| .filter_map(|lock| lock.next_expiration_time()) | ||||||
| .min(); | ||||||
|
|
||||||
| rt_handle | ||||||
| .time() | ||||||
| .inner | ||||||
| .next_wake | ||||||
| .store(next_wake_time(expiration_time)); | ||||||
|
|
||||||
| // Safety: After updating the `next_wake`, we drop all the locks. | ||||||
| drop(locks); | ||||||
| let expiration_time = { | ||||||
| let mut wheels_lock = rt_handle | ||||||
| .time() | ||||||
| .inner | ||||||
| .wheels | ||||||
| .write() | ||||||
| .expect("Timer wheel shards poisened"); | ||||||
| let expiration_time = (0..wheels_lock.get_shard_size()) | ||||||
| .filter_map(|id| { | ||||||
| let wheel = wheels_lock.get_sharded_wheel(id); | ||||||
| wheel.next_expiration_time() | ||||||
| }) | ||||||
| .min(); | ||||||
|
|
||||||
| rt_handle | ||||||
| .time() | ||||||
| .inner | ||||||
| .next_wake | ||||||
| .store(next_wake_time(expiration_time)); | ||||||
|
|
||||||
| expiration_time | ||||||
| }; | ||||||
|
|
||||||
| match expiration_time { | ||||||
| Some(when) => { | ||||||
|
|
@@ -312,7 +321,12 @@ impl Handle { | |||||
| // Returns the next wakeup time of this shard. | ||||||
| pub(self) fn process_at_sharded_time(&self, id: u32, mut now: u64) -> Option<u64> { | ||||||
| let mut waker_list = WakeList::new(); | ||||||
| let mut lock = self.inner.lock_sharded_wheel(id); | ||||||
| let mut wheels_lock = self | ||||||
| .inner | ||||||
| .wheels | ||||||
| .read() | ||||||
| .expect("Timer wheel shards poisened"); | ||||||
| let mut lock = wheels_lock.lock_sharded_wheel(id); | ||||||
|
|
||||||
| if now < lock.elapsed() { | ||||||
| // Time went backwards! This normally shouldn't happen as the Rust language | ||||||
|
|
@@ -334,10 +348,16 @@ impl Handle { | |||||
| if !waker_list.can_push() { | ||||||
| // Wake a batch of wakers. To avoid deadlock, we must do this with the lock temporarily dropped. | ||||||
| drop(lock); | ||||||
| drop(wheels_lock); | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You also need to drop
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sure! Missed that... Implemented in 1610c12. |
||||||
|
|
||||||
| waker_list.wake_all(); | ||||||
|
|
||||||
| lock = self.inner.lock_sharded_wheel(id); | ||||||
| wheels_lock = self | ||||||
| .inner | ||||||
| .wheels | ||||||
| .read() | ||||||
| .expect("Timer wheel shards poisened"); | ||||||
tglane marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||
| lock = wheels_lock.lock_sharded_wheel(id); | ||||||
| } | ||||||
| } | ||||||
| } | ||||||
|
|
@@ -360,7 +380,12 @@ impl Handle { | |||||
| /// `add_entry` must not be called concurrently. | ||||||
| pub(self) unsafe fn clear_entry(&self, entry: NonNull<TimerShared>) { | ||||||
| unsafe { | ||||||
| let mut lock = self.inner.lock_sharded_wheel(entry.as_ref().shard_id()); | ||||||
| let wheels_lock = self | ||||||
| .inner | ||||||
| .wheels | ||||||
| .read() | ||||||
| .expect("Timer wheel shards poisened"); | ||||||
| let mut lock = wheels_lock.lock_sharded_wheel(entry.as_ref().shard_id()); | ||||||
|
|
||||||
| if entry.as_ref().might_be_registered() { | ||||||
| lock.remove(entry); | ||||||
|
|
@@ -383,7 +408,13 @@ impl Handle { | |||||
| entry: NonNull<TimerShared>, | ||||||
| ) { | ||||||
| let waker = unsafe { | ||||||
| let mut lock = self.inner.lock_sharded_wheel(entry.as_ref().shard_id()); | ||||||
| let wheels_lock = self | ||||||
| .inner | ||||||
| .wheels | ||||||
| .read() | ||||||
| .expect("Timer wheel shards poisened"); | ||||||
tglane marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||
|
|
||||||
| let mut lock = wheels_lock.lock_sharded_wheel(entry.as_ref().shard_id()); | ||||||
|
|
||||||
| // We may have raced with a firing/deregistration, so check before | ||||||
| // deregistering. | ||||||
|
|
@@ -443,24 +474,17 @@ impl Handle { | |||||
| // ===== impl Inner ===== | ||||||
|
|
||||||
| impl Inner { | ||||||
| /// Locks the driver's sharded wheel structure. | ||||||
| pub(super) fn lock_sharded_wheel( | ||||||
| &self, | ||||||
| shard_id: u32, | ||||||
| ) -> crate::loom::sync::MutexGuard<'_, Wheel> { | ||||||
| let index = shard_id % (self.wheels.len() as u32); | ||||||
| // Safety: This modulo operation ensures that the index is not out of bounds. | ||||||
| unsafe { self.wheels.get_unchecked(index as usize).lock() } | ||||||
| } | ||||||
|
|
||||||
| // Check whether the driver has been shutdown | ||||||
| pub(super) fn is_shutdown(&self) -> bool { | ||||||
| self.is_shutdown.load(Ordering::SeqCst) | ||||||
| } | ||||||
|
|
||||||
| // Gets the number of shards. | ||||||
| fn get_shard_size(&self) -> u32 { | ||||||
| self.wheels.len() as u32 | ||||||
| self.wheels | ||||||
| .read() | ||||||
| .expect("Timer wheel shards poisened") | ||||||
|
||||||
| .expect("Timer wheel shards poisened") | |
| .expect("Timer wheel shards poisoned") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems reasonable to put it as a member variable into the Inner struct to avoid the lock here. The increase in size of the struct would probably be ok since it's "only" a u32, so I would agree to do it like that if nobody objects.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I included the changes in e9c4351.
I also removed the method to get the shard size from the struct ShardedWheel to prevent duplications since we can get it from the struct Inner everywhere where It's needed.
Uh oh!
There was an error while loading. Please reload this page.