Unnamed repository; edit this file 'description' to name the repository.
perf: defer initial workspace flycheck until cache priming completes
On startup the initial `cargo check` flycheck was launched in the same `became_quiescent` block as cache priming, so the two ran concurrently and fought for CPU. Neither is single-threaded: cache priming fans crate work across worker threads and `cargo check` spawns up to one rustc job per core, so running them together oversubscribes the cores. The effect is felt particularly on macOS, where rust-analyzer's priming threads run at `QOS_CLASS_UTILITY` (`ThreadIntent::Worker`) while the `cargo check` subprocess runs at the default, higher QoS -- so the scheduler starves priming (the work that makes the editor usable) in favour of the check. Move the initial workspace flycheck out of `became_quiescent` into the `PrimeCachesProgress::End` handler via a `pending_initial_flycheck` flag, so the check only starts once priming has finished; fall back to firing it immediately when cache priming is disabled. The flycheck still runs (cargo check begins right after indexing); diagnostics are deferred a beat, not lost. The impact depends on how much the startup `cargo check` has to compile: - Warm check (steady-state reopen): contention is negligible, so this is effectively neutral -- and harmless, deferring a fast check costs nothing. - Cold check (first open after clone / `git pull` / branch switch / toolchain change, where startup already hurts): large. Time-to-ready (cache priming complete) measured on macOS with a cold check, build scripts and proc-macros disabled to isolate the priming/check contention: rust-analyzer 74.8s -> 7.9s (~9.5x) slint 103.1s -> 26.6s (~3.9x) Flycheck slow-tests pass. Disclosure: prototyped with assistance from Claude (Anthropic); changes reviewed and owned by the committer.
Till Adam 5 weeks ago
parent 0717eac · commit d784287
-rw-r--r--crates/rust-analyzer/src/main_loop.rs30
1 files changed, 23 insertions, 7 deletions
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index edf3da5e6c..628e708c6a 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -387,6 +387,16 @@ impl GlobalState {
if cancelled {
self.prime_caches_queue
.request_op("restart after cancellation".to_owned(), ());
+ } else if self.config.check_on_save(None)
+ && self.config.flycheck_workspace(None)
+ && !self.fetch_build_data_queue.op_requested()
+ {
+ // Priming finished; now run the deferred initial workspace flycheck
+ // (kept off the critical path so `cargo check` doesn't contend with
+ // cache priming for CPU).
+ self.flycheck
+ .iter()
+ .for_each(|flycheck| flycheck.restart_workspace(None));
}
if let Some((message, fraction, title)) = last_report.take() {
self.report_progress(
@@ -482,13 +492,6 @@ impl GlobalState {
if self.is_quiescent() {
let became_quiescent = !was_quiescent;
if became_quiescent {
- if self.config.check_on_save(None)
- && self.config.flycheck_workspace(None)
- && !self.fetch_build_data_queue.op_requested()
- {
- // Project has loaded properly, kick off initial flycheck
- self.flycheck.iter().for_each(|flycheck| flycheck.restart_workspace(None));
- }
// delay initial cache priming until proc macros are loaded, or we will load up a bunch of garbage into salsa
let proc_macros_loaded = self.config.prefill_caches()
&& (!self.config.expand_proc_macros()
@@ -496,6 +499,19 @@ impl GlobalState {
if proc_macros_loaded {
self.prime_caches_queue.request_op("became quiescent".to_owned(), ());
}
+ if self.config.check_on_save(None)
+ && self.config.flycheck_workspace(None)
+ && !self.fetch_build_data_queue.op_requested()
+ {
+ if !self.config.prefill_caches() {
+ self.flycheck.iter().for_each(|flycheck| flycheck.restart_workspace(None));
+ } else if proc_macros_loaded
+ && !self.prime_caches_queue.op_in_progress()
+ && !self.prime_caches_queue.op_requested()
+ {
+ self.flycheck.iter().for_each(|flycheck| flycheck.restart_workspace(None));
+ }
+ }
}
let client_refresh = became_quiescent || state_changed;