Skip to content

Concurrently download components of a toolchain #4436

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

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

FranciscoTGouveia
Copy link
Contributor

@FranciscoTGouveia FranciscoTGouveia commented Aug 3, 2025

As of now, each component of a toolchain is downloaded sequentially, leaving room for performance improvements.
This is particularly notorious when considering interleaving downloads with installation, as discussed in #731.

Following the work made in #4426, as the DownloadTracker is now indicatif-based, doing the progress reporting for concurrent downloads is much easier.
As such, this PR comes to enable downloading different components concurrently (possibly closing #3018).

Performance-wise, benchmarks using hyperfine showed a small (1.1x) speedup -- which doesn't seem noticeable but lays the foundations for a future PR to interleave the download with the installation (of components).
The benchmarks were ran 50 times (with 5 warmup runs) over a 50 Mbps connection, each downloading an entire toolchain.

Many thanks to @rami3l and his PR for refactoring part of the test suite for the purpose of this PR.

@FranciscoTGouveia
Copy link
Contributor Author

For reference, the current download of components (sequential) looks like this:

before-ezgif com-speed

... and the changes introduced by this PR go like this:

after-ezgif com-speed

Please note that both animations were sped up and they are not meaningful for comparing performances.

@FranciscoTGouveia FranciscoTGouveia force-pushed the concurrent-downloads branch 2 times, most recently from 3a7ac67 to 10d1514 Compare August 4, 2025 14:37
Some notifications needed to be updated to include the download URL,
enabling the identification of the component being downloaded. This was
necessary for accurate progress reporting of each component.
@FranciscoTGouveia FranciscoTGouveia marked this pull request as ready for review August 5, 2025 13:24
@rami3l rami3l self-assigned this Aug 10, 2025
if let Some(pb) = self.find_progress_bar(file) {
pb.set_style(
ProgressStyle::with_template(
"{msg:>12.bold} downloaded {total_bytes} in {elapsed}.",
Copy link
Member

Choose a reason for hiding this comment

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

Nit: . should be omitted to be coherent with the general notification style.

self.progress_bar.set_length(content_len);
self.progress_bar.set_style(
/// Helper function to find the progress bar for a given file.
fn find_progress_bar(&mut self, file: &str) -> Option<&mut ProgressBar> {
Copy link
Member

Choose a reason for hiding this comment

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

Nit: progress_bar() would be a better name according to the API style guide.

.file_progress_bars
.keys()
.find(|comp| file.contains(*comp))
.cloned()?;
Copy link
Member

Choose a reason for hiding this comment

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

It should not be necessary to clone this component given that we will immediately refer to it.

.await;
for result in results {
match result {
Ok((component, format, downloaded_file, hash)) => {
Copy link
Member

Choose a reason for hiding this comment

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

Nit: Isn't this just let (...) = result?;?

.with_context(|| {
RustupError::ComponentDownloadFailed(component.name(new_manifest))
})?;
Ok::<(Component, CompressionKind, File, String), Error>((
Copy link
Member

Choose a reason for hiding this comment

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

Can this annotation be simpler, say Ok::<_, Error>?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I believe we can simply drop the annotation.

Copy link
Contributor Author

@FranciscoTGouveia FranciscoTGouveia Aug 10, 2025

Choose a reason for hiding this comment

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

As I was addressing the previous nit, I have came to the conclusion that this is necessary. Sorry for the confusion.

@@ -14,11 +14,11 @@ pub enum Notification<'a> {
RemovingDirectory(&'a str, &'a Path),
DownloadingFile(&'a Url, &'a Path),
/// Received the Content-Length of the to-be downloaded data.
DownloadContentLengthReceived(u64),
DownloadContentLengthReceived(u64, &'a str),
Copy link
Member

Choose a reason for hiding this comment

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

Would you mind explaining the meaning of "" here and why they are required? It might be an interesting addition to the doc comments as well.

Copy link
Member

@rami3l rami3l left a comment

Choose a reason for hiding this comment

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

A note to myself: we also have to consider the restriction of concurrent downloads similar to what we have done with threadpooled diskio.

)
.unwrap()
.progress_chars("## "),
);
pb.set_message(component.to_string());
Copy link
Member

Choose a reason for hiding this comment

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

Nit: .to_string() doesn't seem necessary here...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants