-
Notifications
You must be signed in to change notification settings - Fork 979
Interleave downloads with installations when downloading a toolchain #4471
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
base: master
Are you sure you want to change the base?
Interleave downloads with installations when downloading a toolchain #4471
Conversation
c4b68b2
to
c2954ba
Compare
c2954ba
to
1d7e5f5
Compare
0ea9162
to
a2a2c20
Compare
947b8da
to
9e5d0e0
Compare
9e5d0e0
to
dd68c17
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.
Modulo the previously requested changes and the two extra nits, I think this PR is quite ready. Thanks a lot, @FranciscoTGouveia!
d149360
to
1b88c36
Compare
1b88c36
to
5b8e05a
Compare
src/dist/manifestation.rs
Outdated
|
||
let semaphore = Arc::new(Semaphore::new(concurrent_downloads)); | ||
let component_stream = | ||
tokio_stream::iter(components.into_iter()).map(|(component, format, url, hash)| { |
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.
Suggest writing this closure as a named function.
|
||
let mut stream = component_stream.buffered(components_len); | ||
let download_handle = async { | ||
let mut hashes = Vec::new(); |
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.
Suggest writing this as a named function.
src/dist/manifestation.rs
Outdated
} | ||
hashes | ||
}; | ||
let install_handle = async { |
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.
Suggest writing this as a named function.
.keys() | ||
.find(|comp| comp.contains(component)) | ||
.cloned(); | ||
if let Some(key) = key |
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: invert these for early returns.
} | ||
|
||
/// Notifies self that the component has been installed. | ||
pub(crate) fn component_installed(&mut self, component: &str) { |
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.
Can we share the duplicate code?
ProgressStyle::with_template(if pb.position() != 0 { | ||
"{msg:>12.bold} downloaded {total_bytes} in {elapsed}" | ||
} else { | ||
"{msg:>12.bold} component already downloaded" | ||
}) | ||
.unwrap(), |
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.
Again there's a lot of duplication here, suggest adding a little abstraction to make it more obvious what happens here.
tmp_cx: &'a temp::Context, | ||
notify_handler: &'a dyn Fn(Notification<'_>), | ||
changes: Vec<ChangedItem>, | ||
tmp_cx: Arc<temp::Context>, |
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 to me that tmp_cx
, notify_handler
and process
should be bundled up together.
Very excited about these performance improvements! Personally I don't really feel comfortable with the added complexity in the current changes -- I think there should be some more refactoring before this can be cleanly added. |
As I read these results: high speed connections: I suspect some interaction with memory limits showing up in the unpack phase there; I suggest raising the working memory much higher, if you're on a machine that has capacity, to see if thats the case. But also it suggests the default should probably be quite conservative, as we still need to support unpacking on Rasberry PI class machines. |
Even though downloads are done concurrently, the installations are done sequentially. This means that, as downloads complete, they are in a queue (an mpsc channel) waiting to be consumed by the future responsible for the (sequential) installations. There was a need to relax some test cases to allow for uninstall to happen before the downloads.
…ation with a download
5b8e05a
to
7228974
Compare
…how a component was installed
…hread installations Co-authored-by: rami3l <[email protected]>
…nue during installations Co-authored-by: rami3l <[email protected]>
7228974
to
399b575
Compare
// Create a channel to communicate whenever a download is done and the component can be installed | ||
// The `mpsc` channel was used as we need to send many messages from one producer (download's thread) to one consumer (install's thread) | ||
// This is recommended in the official docs: https://docs.rs/tokio/latest/tokio/sync/index.html#mpsc-channel |
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 can't find it now, but I thought I wrote a comment on wondering why we're using a channel here, when we could instead compose the futures that download and install together.
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.
Hmmm would you mind clarifying a little more what you meant by composing the futures?
The current constraints as we previously agreed was that the downloads are concurrent but the installations happening alongside them aren't concurrent within themselves, so I think a channel would be ideal for that case in favor of, say, a mutex.
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.
So you're saying, we only do one installation at a time? If that's the case, I would suggest something more like a FuturesUnordered
containing all the downloads, and then pulling from that to run the installations.
Continuation of #4436.
This change aims to speed up the overall performance of the
rustup [toolchain] install
command by allowing the installation of a component to start immediately after its download completes.This approach is particularly beneficial for slower connections, as it enables installations to progress in parallel with ongoing downloads -- so by the time all downloads are finished, all installations should be completed as well.
NB: This PR is not yet ready for review. I believe this change should be backed by benchmarks and user feedback before moving forward. I’ll share those here in the next couple of days.