2
2
//! sometimes is counter productive when, for example, the first goto definition
3
3
//! request takes longer to compute. This modules implemented prepopulation of
4
4
//! various caches, it's not really advanced at the moment.
5
+ mod topologic_sort;
5
6
6
7
use hir:: db:: DefDatabase ;
7
- use ide_db:: base_db:: { SourceDatabase , SourceDatabaseExt } ;
8
- use rustc_hash:: FxHashSet ;
8
+ use ide_db:: base_db:: {
9
+ salsa:: { Database , ParallelDatabase , Snapshot } ,
10
+ Cancelled , CrateGraph , CrateId , SourceDatabase , SourceDatabaseExt ,
11
+ } ;
12
+ use rustc_hash:: { FxHashMap , FxHashSet } ;
9
13
10
14
use crate :: RootDatabase ;
11
15
@@ -20,29 +24,150 @@ pub struct PrimeCachesProgress {
20
24
pub ( crate ) fn prime_caches ( db : & RootDatabase , cb : & ( dyn Fn ( PrimeCachesProgress ) + Sync ) ) {
21
25
let _p = profile:: span ( "prime_caches" ) ;
22
26
let graph = db. crate_graph ( ) ;
27
+ let to_prime = compute_crates_to_prime ( db, & graph) ;
28
+
29
+ let n_total = to_prime. len ( ) ;
30
+ for ( n_done, & crate_id) in to_prime. iter ( ) . enumerate ( ) {
31
+ let crate_name = graph[ crate_id] . display_name . as_deref ( ) . unwrap_or_default ( ) . to_string ( ) ;
32
+
33
+ cb ( PrimeCachesProgress { on_crate : crate_name, n_done, n_total } ) ;
34
+ // This also computes the DefMap
35
+ db. import_map ( crate_id) ;
36
+ }
37
+ }
38
+
39
+ /// We're indexing many crates.
40
+ #[ derive( Debug ) ]
41
+ pub struct ParallelPrimeCachesProgress {
42
+ /// the crates that we are currently priming.
43
+ pub crates_currently_indexing : Vec < String > ,
44
+ /// the total number of crates we want to prime.
45
+ pub crates_total : usize ,
46
+ /// the total number of crates that have finished priming
47
+ pub crates_done : usize ,
48
+ }
49
+
50
+ pub ( crate ) fn parallel_prime_caches < F > ( db : & RootDatabase , num_worker_threads : u8 , cb : & F )
51
+ where
52
+ F : Fn ( ParallelPrimeCachesProgress ) + Sync + std:: panic:: UnwindSafe ,
53
+ {
54
+ let _p = profile:: span ( "prime_caches" ) ;
55
+
56
+ let graph = db. crate_graph ( ) ;
57
+ let mut crates_to_prime = {
58
+ let crate_ids = compute_crates_to_prime ( db, & graph) ;
59
+
60
+ let mut builder = topologic_sort:: TopologicalSortIter :: builder ( ) ;
61
+
62
+ for & crate_id in & crate_ids {
63
+ let crate_data = & graph[ crate_id] ;
64
+ let dependencies = crate_data
65
+ . dependencies
66
+ . iter ( )
67
+ . map ( |d| d. crate_id )
68
+ . filter ( |i| crate_ids. contains ( i) ) ;
69
+
70
+ builder. add ( crate_id, dependencies) ;
71
+ }
72
+
73
+ builder. build ( )
74
+ } ;
75
+
76
+ crossbeam_utils:: thread:: scope ( move |s| {
77
+ let ( work_sender, work_receiver) = crossbeam_channel:: unbounded ( ) ;
78
+ let ( progress_sender, progress_receiver) = crossbeam_channel:: unbounded ( ) ;
79
+
80
+ enum ParallelPrimeCacheWorkerProgress {
81
+ BeginCrate { crate_id : CrateId , crate_name : String } ,
82
+ EndCrate { crate_id : CrateId , cancelled : bool } ,
83
+ }
84
+
85
+ let prime_caches_worker = move |db : Snapshot < RootDatabase > | {
86
+ while let Ok ( ( crate_id, crate_name) ) = work_receiver. recv ( ) {
87
+ progress_sender
88
+ . send ( ParallelPrimeCacheWorkerProgress :: BeginCrate { crate_id, crate_name } ) ?;
89
+
90
+ let cancelled = Cancelled :: catch ( || {
91
+ // This also computes the DefMap
92
+ db. import_map ( crate_id) ;
93
+ } )
94
+ . is_err ( ) ;
95
+
96
+ progress_sender
97
+ . send ( ParallelPrimeCacheWorkerProgress :: EndCrate { crate_id, cancelled } ) ?;
98
+
99
+ if cancelled {
100
+ break ;
101
+ }
102
+ }
103
+
104
+ Ok :: < _ , crossbeam_channel:: SendError < _ > > ( ( ) )
105
+ } ;
106
+
107
+ for _ in 0 ..num_worker_threads {
108
+ let worker = prime_caches_worker. clone ( ) ;
109
+ let db = db. snapshot ( ) ;
110
+ s. spawn ( move |_| worker ( db) ) ;
111
+ }
112
+
113
+ let crates_total = crates_to_prime. len ( ) ;
114
+ let mut crates_done = 0 ;
115
+
116
+ let mut is_cancelled = false ;
117
+ let mut crates_currently_indexing =
118
+ FxHashMap :: with_capacity_and_hasher ( num_worker_threads as _ , Default :: default ( ) ) ;
119
+
120
+ while !crates_to_prime. is_empty ( ) && !is_cancelled {
121
+ for crate_id in & mut crates_to_prime {
122
+ work_sender
123
+ . send ( (
124
+ crate_id,
125
+ graph[ crate_id] . display_name . as_deref ( ) . unwrap_or_default ( ) . to_string ( ) ,
126
+ ) )
127
+ . ok ( ) ;
128
+ }
129
+
130
+ let worker_progress = match progress_receiver. recv ( ) {
131
+ Ok ( p) => p,
132
+ Err ( _) => break ,
133
+ } ;
134
+ match worker_progress {
135
+ ParallelPrimeCacheWorkerProgress :: BeginCrate { crate_id, crate_name } => {
136
+ crates_currently_indexing. insert ( crate_id, crate_name) ;
137
+ }
138
+ ParallelPrimeCacheWorkerProgress :: EndCrate { crate_id, cancelled } => {
139
+ crates_currently_indexing. remove ( & crate_id) ;
140
+ crates_to_prime. mark_done ( crate_id) ;
141
+ crates_done += 1 ;
142
+ is_cancelled = cancelled;
143
+ }
144
+ } ;
145
+
146
+ let progress = ParallelPrimeCachesProgress {
147
+ crates_currently_indexing : crates_currently_indexing. values ( ) . cloned ( ) . collect ( ) ,
148
+ crates_done,
149
+ crates_total,
150
+ } ;
151
+
152
+ cb ( progress) ;
153
+ db. unwind_if_cancelled ( ) ;
154
+ }
155
+ } )
156
+ . unwrap ( ) ;
157
+ }
158
+
159
+ fn compute_crates_to_prime ( db : & RootDatabase , graph : & CrateGraph ) -> FxHashSet < CrateId > {
23
160
// We're only interested in the workspace crates and the `ImportMap`s of their direct
24
161
// dependencies, though in practice the latter also compute the `DefMap`s.
25
162
// We don't prime transitive dependencies because they're generally not visible in
26
163
// the current workspace.
27
- let to_prime : FxHashSet < _ > = graph
164
+ graph
28
165
. iter ( )
29
166
. filter ( |& id| {
30
167
let file_id = graph[ id] . root_file_id ;
31
168
let root_id = db. file_source_root ( file_id) ;
32
169
!db. source_root ( root_id) . is_library
33
170
} )
34
171
. flat_map ( |id| graph[ id] . dependencies . iter ( ) . map ( |krate| krate. crate_id ) )
35
- . collect ( ) ;
36
-
37
- // FIXME: This would be easy to parallelize, since it's in the ideal ordering for that.
38
- // Unfortunately rayon prevents panics from propagation out of a `scope`, which breaks
39
- // cancellation, so we cannot use rayon.
40
- let n_total = to_prime. len ( ) ;
41
- for ( n_done, & crate_id) in to_prime. iter ( ) . enumerate ( ) {
42
- let crate_name = graph[ crate_id] . display_name . as_deref ( ) . unwrap_or_default ( ) . to_string ( ) ;
43
-
44
- cb ( PrimeCachesProgress { on_crate : crate_name, n_done, n_total } ) ;
45
- // This also computes the DefMap
46
- db. import_map ( crate_id) ;
47
- }
172
+ . collect ( )
48
173
}
0 commit comments