-
Notifications
You must be signed in to change notification settings - Fork 962
Check for updates concurrently #4388
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
Check for updates concurrently #4388
Conversation
b4a3404
to
f8156e7
Compare
After taking a closer look at the code, I realized that To see if this had a practical impact, I re-ran the benchmarks (using the same settings as before) and observed a 3.1x speedup. |
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.
Nice work overall 🎉 I have to suggest some other changes regarding some small details but modulo those everything seems to be going pretty well. Looking forward to landing this patch!
src/cli/rustup_mode.rs
Outdated
let mut update_available = false; | ||
let channels = cfg.list_channels()?; | ||
let num_channels = channels.len(); | ||
if num_channels > 0 { | ||
let mp = MultiProgress::with_draw_target(ProgressDrawTarget::stdout()); | ||
|
||
let mp = if is_a_tty { |
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.
Bonus: cargo
uses the CARGO_TERM_PROGRESS_WHEN
environment variable to control the drawing of the progress bar. I think it'd be interesting to mirror that here as well, maybe in a separate commit or PR.
@@ -223,6 +232,81 @@ impl io::Write for ColorableTerminalLocked { | |||
} | |||
} | |||
|
|||
impl TermLike for ColorableTerminal { | |||
fn width(&self) -> u16 { | |||
Term::stdout().size().1 |
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.
Bonus: cargo
uses the CARGO_TERM_PROGRESS_WIDTH
environment variable to control the terminal width being detected. I think it'd be interesting to mirror that here as well, maybe in a separate commit or PR.
db76e5a
to
86a7a58
Compare
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.
Did you benchmark the performance impact of one or more rustup commands?
Would also be good to add an animation/video of the new output.
src/cli/rustup_mode.rs
Outdated
}) | ||
.collect(); | ||
|
||
let channels = tokio_stream::iter(channels.into_iter().zip(progress_bars)) |
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.
Why are progress_bars
and channels
created in separate loops? Suggest just using for-loops here instead of a trivial combinator chain with a long map()
closure.
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.
@djc We are mapping over a stream rather than an iterator, so doing a plain for
loop might not help here. Depending on the current configuration more combinators will be involved down the line, notably with .buffered()
and .buffer_unordered()
.
To later support `indicatif`'s progress bars, this trait was implemented for the `ColorableTerminal`. It is important to note that `is_a_tty` and `color_choice` will be needed for deciding if progress bars are written to the stdout (or hidden) and if they are styled with colors, respectively.
86a7a58
to
4df6fc8
Compare
In order to evaluate the performance impact of these changes, I ran a series of benchmarks using I compared my modified version of the The results showed a 3.3× speedup over the current implementation. To give a better understanding of what this change implies regarding the user experience, I leave below a short animation. |
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.
That's much clearer, thanks!
This change implied the introduction of the `futures_util` crate. Feedback is greately appreciated on the above. To ensure that there are no hangs, a special "if" was needed to ensure that we never try running `.buffered(0)`. Otherwise, this would cause an hang (kudos to rami3l for helping me out with this). To report progress in a concurrent way whilst maintaining order, the `indicatf` crate was used. Note that, if we are not in a *tty* the progress wil be hidden and will instead be printed after every channels is checked for updates. NB: since we are using `indicatif` to display progress, when running in a *tty*, we can use `buffer_unordered` instead of `buffered`, as `indicatif` already handles the output in the correct order. A quick benchmark (100 runs + 10 warmpu runs in a 50Mbps connection) showed that this small change yields a **1.2x** speedup.
4df6fc8
to
ee063c6
Compare
It seems like a substantial downside to me that the new version takes so long to show any output at all. IMO you should look into fixing that. |
@djc I believe you are reading the gif wrong? This is not a side-by-side comparison; these two commands are executed one after another, both @FranciscoTGouveia It's true that we would like to eliminate all such confusions in the final demo to be used in the blog post/release notes or whatever it may be. |
I’m sorry, I should have showcased the implementation a bit better. In reality, the new version displays output instantaneously. The delay you see between when the left side finishes and the right side starts is simply because I only ran the new version after the base version completed. The pause was caused by me switching panes in |
Ahh, awesome, that's much more impressive! |
As @FranciscoTGouveia and I have agreed to address the bonus points (#4388 (comment), #4388 (comment)) in a later commit, I think this PR is ready to go. Nice work @FranciscoTGouveia ! |
// Ensure that `.buffered()` is never called with 0 as this will cause a hang. | ||
// See: https://github.com/rust-lang/futures-rs/pull/1194#discussion_r209501774 | ||
if num_channels > 0 { | ||
let multi_progress_bars = if is_a_tty { |
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.
Nit: extract the MultiProgress::with_draw_target()
out of the if
(also, I think a match
would look better than an if
for this).
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.
@djc Oops that has happened after the merge so... @FranciscoTGouveia Maybe you could fix it in a new PR? 🙏
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.
Yes, of course! Thank you for pointing this out! :)
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.
Doesn't this get overshadowed by this 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.
Potentially! If so, feel free to ignore my 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.
I realize now that it doesn’t overshadow the previous point, I am sorry.
I have made that change in the follow-up PR.
This change is related to #3132.
Currently, the
rustup check
command verifies updates for each toolchain sequentially.This patch modifies that behavior to perform the checks concurrently.
To implement this change, the
futures_util
crate was introduced. I would appreciate feedback on this choice, and whether there might be a preferable alternative for handling concurrency.My first attempt at adding concurrency caused all output to be printed only after every channel had been checked, which deviated from the current behavior of the command.
To preserve the existing behavior (reporting progress as each toolchain is checked), I tried printing concurrently as well.
However, this led to race conditions, resulting in inconsistent output order.
To address this, I used the
indicatif
crate (cc @djc) to display progress bars while maintaining the correct output order.This PR will soon be updated to ensure progress bars are correctly written to stdout in order to pass the tests.
Regarding performance, using
hyperfine
, a 2.2× speedup was observed compared to the sequential version. These benchmarks were run 100 times (with 5 warmup runs) over a 50 Mbps connection.