Skip to content

Commit 5d35866

Browse files
authored
Merge pull request #21356 from Veykril/push-oumpxzuwmuwp
internal: Collect garbage after events when quiescient
2 parents a03dfd4 + bb05d1a commit 5d35866

13 files changed

Lines changed: 50 additions & 77 deletions

File tree

crates/ide-db/src/apply_change.rs

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,14 @@
11
//! Applies changes to the IDE state transactionally.
22
33
use profile::Bytes;
4-
use salsa::{Database as _, Durability};
4+
use salsa::Database as _;
55

66
use crate::{ChangeWithProcMacros, RootDatabase};
77

88
impl RootDatabase {
9-
pub fn request_cancellation(&mut self) {
10-
let _p = tracing::info_span!("RootDatabase::request_cancellation").entered();
11-
self.synthetic_write(Durability::LOW);
12-
}
13-
149
pub fn apply_change(&mut self, change: ChangeWithProcMacros) {
1510
let _p = tracing::info_span!("RootDatabase::apply_change").entered();
16-
self.request_cancellation();
11+
self.trigger_cancellation();
1712
tracing::trace!("apply_change {:?}", change);
1813
change.apply(self);
1914
}

crates/ide-db/src/prime_caches.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -108,10 +108,9 @@ pub fn parallel_prime_caches(
108108
hir::attach_db(&db, || {
109109
// method resolution is likely to hit all trait impls at some point
110110
// we pre-populate it here as this will hit a lot of parses ...
111-
_ = hir::TraitImpls::for_crate(&db, crate_id);
112-
// we compute the lang items here as the work for them is also highly recursive and will be trigger by the module symbols query
111+
// This also computes the lang items, which is what we want as the work for them is also highly recursive and will be trigger by the module symbols query
113112
// slowing down leaf crate analysis tremendously as we go back to being blocked on a single thread
114-
_ = hir::crate_lang_items(&db, crate_id);
113+
_ = hir::TraitImpls::for_crate(&db, crate_id);
115114
})
116115
});
117116

@@ -271,7 +270,6 @@ pub fn parallel_prime_caches(
271270
}
272271

273272
if crate_def_maps_done == crate_def_maps_total {
274-
// Can we trigger lru-eviction once at this point to reduce peak memory usage?
275273
cb(ParallelPrimeCachesProgress {
276274
crates_currently_indexing: vec![],
277275
crates_done: crate_def_maps_done,

crates/ide/src/lib.rs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ use ide_db::{
6767
FxHashMap, FxIndexSet, LineIndexDatabase,
6868
base_db::{
6969
CrateOrigin, CrateWorkspaceData, Env, FileSet, RootQueryDb, SourceDatabase, VfsPath,
70-
salsa::Cancelled,
70+
salsa::{Cancelled, Database},
7171
},
7272
prime_caches, symbol_index,
7373
};
@@ -199,8 +199,13 @@ impl AnalysisHost {
199199
pub fn per_query_memory_usage(&mut self) -> Vec<(String, profile::Bytes, usize)> {
200200
self.db.per_query_memory_usage()
201201
}
202-
pub fn request_cancellation(&mut self) {
203-
self.db.request_cancellation();
202+
pub fn trigger_cancellation(&mut self) {
203+
self.db.trigger_cancellation();
204+
}
205+
pub fn trigger_garbage_collection(&mut self) {
206+
self.db.trigger_lru_eviction();
207+
// SAFETY: `trigger_lru_eviction` triggers cancellation, so all running queries were canceled.
208+
unsafe { hir::collect_ty_garbage() };
204209
}
205210
pub fn raw_database(&self) -> &RootDatabase {
206211
&self.db

crates/intern/src/gc.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -87,20 +87,20 @@ pub trait GcInternedSliceVisit: SliceInternable {
8787
#[derive(Default)]
8888
pub struct GarbageCollector {
8989
alive: FxHashSet<usize>,
90-
storages: Vec<Box<dyn Storage + Send + Sync>>,
90+
storages: Vec<&'static (dyn Storage + Send + Sync)>,
9191
}
9292

9393
impl GarbageCollector {
9494
pub fn add_storage<T: Internable + GcInternedVisit>(&mut self) {
9595
const { assert!(T::USE_GC) };
9696

97-
self.storages.push(Box::new(InternedStorage::<T>(PhantomData)));
97+
self.storages.push(&InternedStorage::<T>(PhantomData));
9898
}
9999

100100
pub fn add_slice_storage<T: SliceInternable + GcInternedSliceVisit>(&mut self) {
101101
const { assert!(T::USE_GC) };
102102

103-
self.storages.push(Box::new(InternedSliceStorage::<T>(PhantomData)));
103+
self.storages.push(&InternedSliceStorage::<T>(PhantomData));
104104
}
105105

106106
/// # Safety
@@ -111,11 +111,12 @@ impl GarbageCollector {
111111
/// - [`GcInternedVisit`] and [`GcInternedSliceVisit`] must mark all values reachable from the node.
112112
pub unsafe fn collect(mut self) {
113113
let total_nodes = self.storages.iter().map(|storage| storage.len()).sum();
114-
self.alive = FxHashSet::with_capacity_and_hasher(total_nodes, FxBuildHasher);
114+
self.alive.clear();
115+
self.alive.reserve(total_nodes);
115116

116117
let storages = std::mem::take(&mut self.storages);
117118

118-
for storage in &storages {
119+
for &storage in &storages {
119120
storage.mark(&mut self);
120121
}
121122

crates/rust-analyzer/src/cli/analysis_stats.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -354,11 +354,10 @@ impl flags::AnalysisStats {
354354
self.run_term_search(&workspace, db, &vfs, &file_ids, verbosity);
355355
}
356356

357-
hir::clear_tls_solver_cache();
358-
unsafe { hir::collect_ty_garbage() };
359-
360357
let db = host.raw_database_mut();
361358
db.trigger_lru_eviction();
359+
hir::clear_tls_solver_cache();
360+
unsafe { hir::collect_ty_garbage() };
362361

363362
let total_span = analysis_sw.elapsed();
364363
eprintln!("{:<20} {total_span}", "Total:");

crates/rust-analyzer/src/cli/rustc_tests.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ impl Tester {
185185

186186
if !worker.is_finished() {
187187
// attempt to cancel the worker, won't work for chalk hangs unfortunately
188-
self.host.request_cancellation();
188+
self.host.trigger_garbage_collection();
189189
}
190190
worker.join().and_then(identity)
191191
});

crates/rust-analyzer/src/config.rs

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -98,13 +98,6 @@ config_data! {
9898
/// Code's `files.watcherExclude`.
9999
files_exclude | files_excludeDirs: Vec<Utf8PathBuf> = vec![],
100100

101-
/// This config controls the frequency in which rust-analyzer will perform its internal Garbage
102-
/// Collection. It is specified in revisions, roughly equivalent to number of changes. The default
103-
/// is 1000.
104-
///
105-
/// Setting a smaller value may help limit peak memory usage at the expense of speed.
106-
gc_frequency: usize = 1000,
107-
108101
/// If this is `true`, when "Goto Implementations" and in "Implementations" lens, are triggered on a `struct` or `enum` or `union`, we filter out trait implementations that originate from `derive`s above the type.
109102
gotoImplementations_filterAdjacentDerives: bool = false,
110103

@@ -1712,10 +1705,6 @@ impl Config {
17121705
&self.caps
17131706
}
17141707

1715-
pub fn gc_freq(&self) -> usize {
1716-
*self.gc_frequency()
1717-
}
1718-
17191708
pub fn assist(&self, source_root: Option<SourceRootId>) -> AssistConfig {
17201709
AssistConfig {
17211710
snippet_cap: self.snippet_cap(),

crates/rust-analyzer/src/global_state.rs

Lines changed: 9 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use hir::ChangeWithProcMacros;
1515
use ide::{Analysis, AnalysisHost, Cancellable, FileId, SourceRootId};
1616
use ide_db::{
1717
MiniCore,
18-
base_db::{Crate, ProcMacroPaths, SourceDatabase},
18+
base_db::{Crate, ProcMacroPaths, SourceDatabase, salsa::Revision},
1919
};
2020
use itertools::Itertools;
2121
use load_cargo::SourceRootConfig;
@@ -193,15 +193,14 @@ pub(crate) struct GlobalState {
193193
/// which will usually end up causing a bunch of incorrect diagnostics on startup.
194194
pub(crate) incomplete_crate_graph: bool,
195195

196-
pub(crate) revisions_until_next_gc: usize,
197-
198196
pub(crate) minicore: MiniCoreRustAnalyzerInternalOnly,
197+
pub(crate) last_gc_revision: Revision,
199198
}
200199

201200
// FIXME: This should move to the VFS once the rewrite is done.
202201
#[derive(Debug, Clone, Default)]
203202
pub(crate) struct MiniCoreRustAnalyzerInternalOnly {
204-
pub(crate) minicore_text: Option<String>,
203+
pub(crate) minicore_text: Option<Arc<str>>,
205204
}
206205

207206
/// An immutable snapshot of the world's state at a point in time.
@@ -258,6 +257,8 @@ impl GlobalState {
258257

259258
let (discover_sender, discover_receiver) = unbounded();
260259

260+
let last_gc_revision = analysis_host.raw_database().nonce_and_revision().1;
261+
261262
let mut this = GlobalState {
262263
sender,
263264
req_queue: ReqQueue::default(),
@@ -321,8 +322,7 @@ impl GlobalState {
321322
incomplete_crate_graph: false,
322323

323324
minicore: MiniCoreRustAnalyzerInternalOnly::default(),
324-
325-
revisions_until_next_gc: config.gc_freq(),
325+
last_gc_revision,
326326
};
327327
// Apply any required database inputs from the config.
328328
this.update_configuration(config);
@@ -347,11 +347,11 @@ impl GlobalState {
347347

348348
let (change, modified_rust_files, workspace_structure_change) =
349349
self.cancellation_pool.scoped(|s| {
350-
// start cancellation in parallel, this will kick off lru eviction
350+
// start cancellation in parallel,
351351
// allowing us to do meaningful work while waiting
352352
let analysis_host = AssertUnwindSafe(&mut self.analysis_host);
353353
s.spawn(thread::ThreadIntent::LatencySensitive, || {
354-
{ analysis_host }.0.request_cancellation()
354+
{ analysis_host }.0.trigger_cancellation()
355355
});
356356

357357
// downgrade to read lock to allow more readers while we are normalizing text
@@ -440,14 +440,6 @@ impl GlobalState {
440440

441441
self.analysis_host.apply_change(change);
442442

443-
if self.revisions_until_next_gc == 0 {
444-
// SAFETY: Just changed some database inputs, all queries were canceled.
445-
unsafe { hir::collect_ty_garbage() };
446-
self.revisions_until_next_gc = self.config.gc_freq();
447-
} else {
448-
self.revisions_until_next_gc -= 1;
449-
}
450-
451443
if !modified_ratoml_files.is_empty()
452444
|| !self.config.same_source_root_parent_map(&self.local_roots_parent_map)
453445
{
@@ -741,7 +733,7 @@ impl GlobalState {
741733

742734
impl Drop for GlobalState {
743735
fn drop(&mut self) {
744-
self.analysis_host.request_cancellation();
736+
self.analysis_host.trigger_cancellation();
745737
}
746738
}
747739

crates/rust-analyzer/src/main_loop.rs

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use std::{
99
};
1010

1111
use crossbeam_channel::{Receiver, never, select};
12-
use ide_db::base_db::{SourceDatabase, VfsPath, salsa::Database as _};
12+
use ide_db::base_db::{SourceDatabase, VfsPath};
1313
use lsp_server::{Connection, Notification, Request};
1414
use lsp_types::{TextDocumentIdentifier, notification::Notification as _};
1515
use stdx::thread::ThreadIntent;
@@ -383,7 +383,7 @@ impl GlobalState {
383383
));
384384
}
385385
PrimeCachesProgress::End { cancelled } => {
386-
self.analysis_host.raw_database_mut().trigger_lru_eviction();
386+
self.analysis_host.trigger_garbage_collection();
387387
self.prime_caches_queue.op_completed(());
388388
if cancelled {
389389
self.prime_caches_queue
@@ -535,6 +535,16 @@ impl GlobalState {
535535
if project_or_mem_docs_changed && self.config.test_explorer() {
536536
self.update_tests();
537537
}
538+
539+
let current_revision = self.analysis_host.raw_database().nonce_and_revision().1;
540+
// no work is currently being done, now we can block a bit and clean up our garbage
541+
if self.task_pool.handle.is_empty()
542+
&& self.fmt_pool.handle.is_empty()
543+
&& current_revision != self.last_gc_revision
544+
{
545+
self.analysis_host.trigger_garbage_collection();
546+
self.last_gc_revision = current_revision;
547+
}
538548
}
539549

540550
self.cleanup_discover_handles();
@@ -907,7 +917,8 @@ impl GlobalState {
907917
// Not a lot of bad can happen from mistakenly identifying `minicore`, so proceed with that.
908918
self.minicore.minicore_text = contents
909919
.as_ref()
910-
.and_then(|contents| String::from_utf8(contents.clone()).ok());
920+
.and_then(|contents| str::from_utf8(contents).ok())
921+
.map(triomphe::Arc::from);
911922
}
912923

913924
let path = VfsPath::from(path);

crates/rust-analyzer/src/task_pool.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,10 @@ impl<T> TaskPool<T> {
4343
pub(crate) fn len(&self) -> usize {
4444
self.pool.len()
4545
}
46+
47+
pub(crate) fn is_empty(&self) -> bool {
48+
self.pool.is_empty()
49+
}
4650
}
4751

4852
/// `DeferredTaskQueue` holds deferred tasks.

0 commit comments

Comments
 (0)