Consolidate concurrency limits (#3493)

## Summary

This PR consolidates the concurrency limits used throughout `uv` and
exposes two limits, `UV_CONCURRENT_DOWNLOADS` and
`UV_CONCURRENT_BUILDS`, as environment variables.

Currently, `uv` has a number of concurrent streams that it buffers using
relatively arbitrary limits for backpressure. However, many of these
limits are conflated. We run a relatively small number of tasks overall
and should start most things as soon as possible. What we really want to
limit are three separate operations:
- File I/O. This is managed by tokio's blocking pool and we should not
really have to worry about it.
- Network I/O.
- Python build processes.

Because the current limits span a broad range of tasks, it's possible
that a limit meant for network I/O is occupied by tasks performing
builds, reading from the file system, or even waiting on a `OnceMap`. We
also don't limit build processes that end up being required to perform a
download. While this may not pose a performance problem because our
limits are relatively high, it does mean that the limits do not do what
we want, making it tricky to expose them to users
(https://github.com/astral-sh/uv/issues/1205,
https://github.com/astral-sh/uv/issues/3311).

After this change, the limits on network I/O and build processes are
centralized and managed by semaphores. All other tasks are unbuffered
(note that these tasks are still bounded, so backpressure should not be
a problem).
This commit is contained in:
Ibraheem Ahmed 2024-05-10 12:43:08 -04:00 committed by GitHub
parent eab2b832a6
commit 783df8f657
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
35 changed files with 575 additions and 218 deletions

View file

@ -0,0 +1,35 @@
use std::num::NonZeroUsize;
/// Concurrency limit settings.
#[derive(Copy, Clone, Debug)]
pub struct Concurrency {
/// The maximum number of concurrent downloads.
///
/// Note this value must be non-zero.
pub downloads: usize,
/// The maximum number of concurrent builds.
///
/// Note this value must be non-zero.
pub builds: usize,
}
impl Default for Concurrency {
fn default() -> Self {
Concurrency {
downloads: Concurrency::DEFAULT_DOWNLOADS,
builds: Concurrency::default_builds(),
}
}
}
impl Concurrency {
// The default concurrent downloads limit.
pub const DEFAULT_DOWNLOADS: usize = 50;
// The default concurrent builds limit.
pub fn default_builds() -> usize {
std::thread::available_parallelism()
.map(NonZeroUsize::get)
.unwrap_or(1)
}
}

View file

@ -1,5 +1,6 @@
pub use authentication::*;
pub use build_options::*;
pub use concurrency::*;
pub use config_settings::*;
pub use constraints::*;
pub use name_specifiers::*;
@ -10,6 +11,7 @@ pub use target_triple::*;
mod authentication;
mod build_options;
mod concurrency;
mod config_settings;
mod constraints;
mod name_specifiers;