-
Notifications
You must be signed in to change notification settings - Fork 2.4k
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
Bypass rustup wrapper when invoking rustc #10986
Comments
If I remember correctly rust-lang/rustup#2626 is related to this and was fixed in rustup version |
If its possible to do this in cargo, I wonder if this is another viable option for solving the problem. |
🤔 Alternative 4a - run
|
I like this one. The
Yeah, when using a linked toolchain, the default cargo is combined with the local rustc. This would break for the solution proposed by @davidlattimore, but works just fine for all alternatives I think. |
This seems simple enough that I just went ahead and started implementing it: rust-lang/rust#100681; #10998 |
I personally would lean towards option 3b. I wouldn't think the scenario of directly executing cargo without rustup, but wanting rustc to be driven by rustup is too common (mostly applicable to those developing cargo itself?). Rustup already knows where things are, it might as well just inform the tools. Another option I don't see mentioned is for rustup to alter PATH to put the toolchain first. Rustup used to work like that at one point, but then it was removed due to rust-lang/rustup#809. However, I didn't see in that discussion why rustup didn't just prepend the actual toolchain directory first, instead of the cargo fallback toolchain. If that is viable, I might prefer that since then no tools need to even bother with knowing how to set things up. |
A much more complicated option 5: Implement a fork server for rustc. This has the advantage that commonly used data (e.g. |
I don't think having a fork server supersedes this change as presumably the executable talking to the fork server would also be behind the rustup shim. |
Depends on how you talk to the fork-server. If cargo can open a socket to it to send commands then there's no further process spawning overhead (other than forks). |
I don't think Windows has an equivalent to a fork server. Windows system libraries aren't capable of being forked into two the way that Unix system libraries are. Of course, you can do something like a fork server, where you spawn a new process and give it a copy of the dynamic set up state over a channel/port/socket/whatever. Doing so as a "fork server" is perhaps better than doing so from the outside world. But the benefit over just doing the regular startup is much diminished. |
Windows has to have a fork implementation to support WSL1 and previously SFU. But I don't know what that would do to win32 processes. |
Yes, the kernel supports a fork. It's the system libraries which will fall over and cause problems. https://stackoverflow.com/a/62888362
|
Also WSL1 uses picoprocesses as originating from project drawbridge, not regular NT processes. |
WSL is a unix, so has |
https://twitter.com/tiraniddo/status/1100151078677610496?s=20&t=gZ-8Q2Op73DA5IuBRFeYbg
https://twitter.com/AmarSaar/status/1100182770175877121?s=20&t=gZ-8Q2Op73DA5IuBRFeYbg
It exists, but it's an undocumented API surface. Again, just because you can doesn't mean that anything is going to work if you try to talk to the OS, which rustc very much needs to do. |
One potential issue with doing something like that is that it would affect any nested invocations of cargo or rustc. e.g. if someone runs |
Or also the initial reason rust-lang/rustup#2958 was reverted; people have been doing |
…enkov Add `rustc --print rustc-path` Related: - rust-lang/cargo#10986 - rust-lang/rustup#3035 Goal: Like the original rust-lang/rustup#2958, the goal is to enable `cargo` to call `rustc` directly, rather than through the `rustup` shim. Solution: `cargo` asks `rustc` to tell it what executable to run. Sometime early in compilation, `cargo` will run `$(RUSTC_WRAPPER) $(RUSTC ?: rustc) --print rustc-path`[^1]. Further calls to `rustc` to do execution will use the resolved printed executable path rather than continuing to call the "input `rustc` path," which will allow it to bypass the `rustup` shim. The cargo side is rust-lang/cargo#10998. [^1]: This can potentially be combined with other `--print`s, as well! This is a tiny patch, so I've implemented it as insta-stable; this will need signoff probably from both the compiler and cargo teams. I'm working on the cargo side of the patch currently, but wanted to get this up ASAP. cc `@davidlattimore` `@bjorn3` `@rust-lang/cargo` `@rust-lang/compiler` (sorry for the big ping list 😅)
I like that alternative 4a works even if you bypass the rustup wrapper when invoking cargo. It's unfortunate though that for the common use-case of running Option 3b would allow the outer rustup ( It's a shame that there doesn't seem to be a single option that has all the following properties:
I guess doing 4a now doesn't preclude also doing 3b later in order to avoid the second rustup invocation. Or perhaps once 4a is done, the next thing would be to try to speed up rustup itself so that the second (and first) invocation doesn't matter so much. |
…enkov Add `rustc --print rustc-path` Related: - rust-lang/cargo#10986 - rust-lang/rustup#3035 Goal: Like the original rust-lang/rustup#2958, the goal is to enable `cargo` to call `rustc` directly, rather than through the `rustup` shim. Solution: `cargo` asks `rustc` to tell it what executable to run. Sometime early in compilation, `cargo` will run `$(RUSTC_WRAPPER) $(RUSTC ?: rustc) --print rustc-path`[^1]. Further calls to `rustc` to do execution will use the resolved printed executable path rather than continuing to call the "input `rustc` path," which will allow it to bypass the `rustup` shim. The cargo side is rust-lang/cargo#10998. [^1]: This can potentially be combined with other `--print`s, as well! This is a tiny patch, so I've implemented it as insta-stable; this will need signoff probably from both the compiler and cargo teams. I'm working on the cargo side of the patch currently, but wanted to get this up ASAP. cc `@davidlattimore` `@bjorn3` `@rust-lang/cargo` `@rust-lang/compiler` (sorry for the big ping list 😅)
…enkov Add `rustc --print rustc-path` Related: - rust-lang/cargo#10986 - rust-lang/rustup#3035 Goal: Like the original rust-lang/rustup#2958, the goal is to enable `cargo` to call `rustc` directly, rather than through the `rustup` shim. Solution: `cargo` asks `rustc` to tell it what executable to run. Sometime early in compilation, `cargo` will run `$(RUSTC_WRAPPER) $(RUSTC ?: rustc) --print rustc-path`[^1]. Further calls to `rustc` to do execution will use the resolved printed executable path rather than continuing to call the "input `rustc` path," which will allow it to bypass the `rustup` shim. The cargo side is rust-lang/cargo#10998. [^1]: This can potentially be combined with other `--print`s, as well! This is a tiny patch, so I've implemented it as insta-stable; this will need signoff probably from both the compiler and cargo teams. I'm working on the cargo side of the patch currently, but wanted to get this up ASAP. cc ``@davidlattimore`` ``@bjorn3`` ``@rust-lang/cargo`` ``@rust-lang/compiler`` (sorry for the big ping list 😅)
…enkov Add `rustc --print rustc-path` Related: - rust-lang/cargo#10986 - rust-lang/rustup#3035 Goal: Like the original rust-lang/rustup#2958, the goal is to enable `cargo` to call `rustc` directly, rather than through the `rustup` shim. Solution: `cargo` asks `rustc` to tell it what executable to run. Sometime early in compilation, `cargo` will run `$(RUSTC_WRAPPER) $(RUSTC ?: rustc) --print rustc-path`[^1]. Further calls to `rustc` to do execution will use the resolved printed executable path rather than continuing to call the "input `rustc` path," which will allow it to bypass the `rustup` shim. The cargo side is rust-lang/cargo#10998. [^1]: This can potentially be combined with other `--print`s, as well! This is a tiny patch, so I've implemented it as insta-stable; this will need signoff probably from both the compiler and cargo teams. I'm working on the cargo side of the patch currently, but wanted to get this up ASAP. cc ```@davidlattimore``` ```@bjorn3``` ```@rust-lang/cargo``` ```@rust-lang/compiler``` (sorry for the big ping list 😅)
…enkov Add `rustc --print rustc-path` Related: - rust-lang/cargo#10986 - rust-lang/rustup#3035 Goal: Like the original rust-lang/rustup#2958, the goal is to enable `cargo` to call `rustc` directly, rather than through the `rustup` shim. Solution: `cargo` asks `rustc` to tell it what executable to run. Sometime early in compilation, `cargo` will run `$(RUSTC_WRAPPER) $(RUSTC ?: rustc) --print rustc-path`[^1]. Further calls to `rustc` to do execution will use the resolved printed executable path rather than continuing to call the "input `rustc` path," which will allow it to bypass the `rustup` shim. The cargo side is rust-lang/cargo#10998. [^1]: This can potentially be combined with other `--print`s, as well! This is a tiny patch, so I've implemented it as insta-stable; this will need signoff probably from both the compiler and cargo teams. I'm working on the cargo side of the patch currently, but wanted to get this up ASAP. cc ````@davidlattimore```` ````@bjorn3```` ````@rust-lang/cargo```` ````@rust-lang/compiler```` (sorry for the big ping list 😅)
Problem
I was just using flamegraph to profile a clean
cargo build
and observed that more than 4% of time was being spent parsing rustup configuration files. For a cleancargo check
it was about 7%.This seems to be because when you run
cargo
, it invokes rustup which then figures out which actual cargo binary to invoke (fine so far), then cargo invokesrustc
many times and each time it does so, it's invoking rustup again, which has to go and reparse configuration files to figure out which rustc binary to invoke.See
rustup_init::main
on the right (7.54%)This was produced by running:
sudo apt install mold cargo install flamegraph git clone https://github.com/ebobby/simple-raytracer.git cd simple-raytracer cargo clean flamegraph -- mold -run cargo check
To see the potential savings of bypassing rustup, we can set the
RUSTC
environment variable to point directly at rustc.With rustup:
Bypassing rustup:
So about a 9.5% speedup. I'd expect for crates with relatively few, large dependencies, the time spent by rustup would be less as a percentage. For crates with lots of small dependencies it could be more.
For a trivial (hello world) binary, a warm
cargo check
with a single line change is 101.5ms. If we setRUSTC
to bypass rustup, this drops to 67.6ms.The above times were all on my laptop. For an extra datapoint, I tried building nushell on a relatively powerful desktop with lots of RAM and CPUs. Cold
cargo check
went from 23.061 s ± 0.169 s to 22.313 s ± 0.188 s (3.4% speedup). A warmcargo check
(with trivial one line change) went from 613.6 ms ± 3.6 ms to 582.3 ms ± 7.4 ms (5.4% speedup).Steps performed by cargo to determine what
rustc
to run:RUSTC
if that's setbuild.rustc
from.cargo/config.toml
if setrustc
and find it from$PATH
Other tools (e.g
rustdoc
) follow the same pattern.rustc
is actually a little different in that steps 1 and 2 are first performed forrustc_wrapper
andrustc_workspace_wrapper
. This doesn't affect the proposed solution or the alternatives.Proposed solution
Add step 3: Use tool (e.g.
rustc
) from the directory that contains the currentcargo
binary in preference to usingPATH
.Draft commit - still needs testing, updating of tests etc.
The main change to behaviour, would be a scenario like the following:
/aaa
/bbb
PATH=/aaa:/bbb
orPATH=/aaa
- i.e./aaa
is the first or only toolchain on the path./bbb/cargo build
/bbb/cargo
would have invoked/aaa/rustc
because that's what's on the PATH/bbb/cargo
would invoke/bbb/rustc
While any change has the potential to break someone, the new behaviour actually seems less surprising to me. From the perspective of someone who only uses
cargo
,rustc
could be considered an internal implementation detail and one might expect invokingcargo
via an explicit path to use the associatedrustc
and other related tools.Alternatives considered
Alternative 1 - make rustup faster
This alone probably doesn't get us all the speedup we'd like. Even if we sped up rustup by a factor of 2, we'd still be spending a significant amount of time once we invoke it once for every
rustc
invocation.Alternative 2 - change rustup to set the
RUSTC
environment variableThis would break crates that set
build.rustc
in.cargo/config.toml
since the value inRUSTC
would override it. So this isn't really an option.Alternative 3a - rustup sets new environment variable that's then used by cargo
Rustup could set
DEFAULT_RUSTC
, which would be likeRUSTC
but would, if set, be used at step 3.A downside is that this option treats rustc differently to the other tools that rustup wraps - unless we also add environment variables for other tools as well. e.g.
DEFAULT_RUSTDOC
- but that is messy and verbose.Alternative 3b - environment variable is the directory containing the tools
Similar to alternative 3a, but instead of having the new environment variable point to RUSTC, have it point to the directory. This would allow all tools to be treated consistently.
One downside of this option (and 3a) is that if the user bypasses the rustup wrapper by explicitly invoking cargo, then cargo would revert to invoking
rustup
for every call torustc
.Alternative 4 - cargo could run
rustup which rustc
onceIf cargo ran
rustup which rustc
, it could then (if the command succeeded) use the result at step 3.We'd still be running
rustup
twice - once to determine whichcargo
binary to invoke, then again withincargo
. It's a lot better than invoking it N times though.Notes
No response
The text was updated successfully, but these errors were encountered: