-
Notifications
You must be signed in to change notification settings - Fork 342
perf: Only register wakeups in the consumer when no messages are ready #320
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
Conversation
By using tokio's `Notify` type and it's capability to get notified even if no one was available to be notified when the notification fires we avoid the need for locking and we keep synchronization out of the common path (a message being available). This does force tokio to be pulled in for non-tokio users, however they will only get the now required `sync` feature which excludes most of tokio. https://docs.rs/tokio/1.0.1/tokio/sync/struct.Notify.html
Would let me name the `Notified` future in fede1024/rust-rdkafka#320 instead of boxing it.
benesch
left a comment
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.
Thanks! This is a good idea. I landed a very simple version of this optimization in #321.
I'm not sure whether Tokio's Notified struct will buy us much on top of that. Did you happen to benchmark the performance of this change? My preference is to avoid introducing the dependency on tokio/sync unless the performance gains are substantial—there are some async-std/smol folks who really dislike having Tokio in the tree. And it looks to me like Notified will acquire a lock in the pending case anyway (https://github.com/tokio-rs/tokio/blob/3b6bee822dfe70caea7eb22b51fefb28d162f966/tokio/src/sync/notify.rs#L555), though it's complicated enough that I'm not totally sure.
| Some(message) => { | ||
| // More messages were available, notify all other waiters | ||
| self.consumer.context().wakers.notify_waiters(); | ||
| return Poll::Ready(Some(message)); |
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 don't think I get this. In the typical case, when the message queue becomes non-empty, we'll wake up precisely one waiter. Then we'll end up here, because we know there is at least one message available, at which point we'll notify all the wakers. So why not just call wakers.notify_wakers from the context to start?
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.
The problem with calling notify_waiters is that it will only notify the currently registered waiters, whereas notify_one will register one waiter or the next waiter, if none are currently waiting.
We also can't call both notify functions when the non-empty callback is called since it would be possible to race it such that
Task 1 sees no message, returns from poll
Non empty callback notifies all waiters
Task 1 acquires the handle from `notified()` but won't be woken up until the next non-empty callback.
Always notifying when we know there are likely messages solves this issue.
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.
Ah, ok, thanks for explaining.
No, this is mostly speculative and it is nice to remove locking in async code.
I can see the optics being a bit bad, however this only actually includes and compiles tokio's
But the common case is that the we do not have a |
Ok, yep, that makes sense. I think the combination of the Tokio dep + the extra complexity makes me leery of merging this, but I'd be happy to be swayed by performance numbers. |
|
Thanks again for this @Marwes but I think I'm going to hold off on the extra complexity unless there is compelling improved performance. I'm afraid I don't have time to do that investigation myself, but I'd be happy to reconsider if you provide evidence! |
By using tokio's
Notifytype and it's capability to get notified evenif no one was available to be notified when the notification fires we
avoid the need for locking and we keep synchronization out of the
common path (a message being available).
This does force tokio to be pulled in for non-tokio users, however they
will only get the now required
syncfeature which excludes most oftokio.
https://docs.rs/tokio/1.0.1/tokio/sync/struct.Notify.html