Open TomFryersMidsummer opened 1 month ago
@TomFryersMidsummer thanks fort the report. What version of rustfmt are you using? How are you calling rustfmt in your scripts? Do you have a rustfmt.toml
file in the project? have you profiled rustfmt to determine where it's spending the most time?
Also, running rustfmt on lib.rs
is going to format the entire project, not just the lib.rs
file. You can see this if you add the -v
flag to your call e.g. rustfmt -v src/lib.rs
.
If you're using nightly rustfmt you can pass along the --unstable-features --skip-children
flags to only format the current file.
I'm using rustfmt 1.8.0-nightly (7608018cbd 2024-09-29)
. I have rustfmt.toml
(edition = "2021"
), but running outside the project directory makes no difference.
I’m passing source code to rustfmt
on standard input, but the problem is the same if I put it in a file instead. The issue arises even with no input: rustfmt --help
.
I haven’t profiled Rustfmt, but I have noticed that it’s nearly twice as fast if I specify the toolchain manually, with +nightly
or +stable
, so perhaps toolchain selection is taking a very long time.
Better still: echo | ~/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/bin/rustfmt
is only 10 ms.
Largely, it looks like this is caused by this Rustup issue. But it would still be nice to be down closer to Ruff when calling the binary without the wrapper.
That's all very helpful info!
I’m passing source code to rustfmt on standard input, but the problem is the same if I put it in a file instead
are you making those invocations sequentially or in parallel? in the latter, are you invoking rustfmt multiple times and passing it one file each time or invoking rustfmt once and passing the full list of file paths?
I'd imagine there's some level of bootstrapping overhead each time that establishes a runtime floor, but unless there's some really straightforward low hanging fruit I don't think this is something we can really do much about, or at least I don't think we've got the spare capacity to try to optimize away a couple dozen milliseconds
are you making those invocations sequentially or in parallel?
In parallel.
in the latter, are you invoking rustfmt multiple times and passing it one file each time or invoking rustfmt once and passing the full list of file paths?
I'm passing it one file each time on standard input. I couldn't find a way to get Rustfmt to take more than one file on standard input. I could save everything to temporary files on tmpfs, pass this list to Rustfmt, then read them all back, but that's a bit of a silly workaround, with its own overheads. I could also concatenate them, with some special comment as a separator, and try to split them back up, but that doesn't work if a file contains syntax errors.
Rustfmt has quite high per-run overhead. For example, running repeated benchmarks on my machine gives the following means:
echo | rustfmt
: 48 mscat lib.rs | rustfmt
: 55 msHere,
lib.rs
is rustfmt’s own 670-line file. 55 ms for 670 lines is pretty good! But 48 ms to format the empty string doesn’t seem quite so speedy.Some other formatters have a much lower overhead:
ruff format -
, for instance, takes 4 ms. Although some are far worse:echo | prettier --parser babel
takes 230 ms.This can make quite a difference when using
rustfmt
in scripts. (I have a script that formats proptest function bodies, which callsrustfmt
452 times.)