@@ -37,18 +37,23 @@ use common::PerfLogger;
37
37
use common:: WithDiagnostics ;
38
38
use dashmap:: mapref:: entry:: Entry ;
39
39
use dashmap:: DashSet ;
40
+ use dependency_analyzer:: get_ir_definition_references;
40
41
use fnv:: FnvBuildHasher ;
41
42
use fnv:: FnvHashMap ;
42
43
use fnv:: FnvHashSet ;
43
44
pub use generate_artifacts:: generate_artifacts;
44
45
pub use generate_artifacts:: generate_preloadable_query_parameters_artifact;
45
46
pub use generate_artifacts:: Artifact ;
46
47
pub use generate_artifacts:: ArtifactContent ;
48
+ use graphql_ir:: ExecutableDefinition ;
49
+ use graphql_ir:: ExecutableDefinitionName ;
47
50
use graphql_ir:: FragmentDefinitionNameSet ;
48
51
use graphql_ir:: Program ;
52
+ use indexmap:: IndexSet ;
49
53
use log:: debug;
50
54
use log:: info;
51
55
use log:: warn;
56
+ use petgraph:: unionfind:: UnionFind ;
52
57
use rayon:: iter:: IntoParallelRefIterator ;
53
58
use rayon:: slice:: ParallelSlice ;
54
59
use relay_codegen:: Printer ;
@@ -83,7 +88,7 @@ use crate::file_source::SourceControlUpdateStatus;
83
88
use crate :: graphql_asts:: GraphQLAsts ;
84
89
85
90
type BuildProjectOutput = WithDiagnostics < ( ProjectName , Arc < SDLSchema > , Programs , Vec < Artifact > ) > ;
86
- type BuildProgramsOutput = WithDiagnostics < ( Programs , Arc < SourceHashes > ) > ;
91
+ type BuildProgramsOutput = WithDiagnostics < ( Vec < Programs > , Arc < SourceHashes > ) > ;
87
92
88
93
pub enum BuildProjectFailure {
89
94
Error ( BuildProjectError ) ,
@@ -141,6 +146,87 @@ pub fn build_raw_program(
141
146
Ok ( ( program, source_hashes) )
142
147
}
143
148
149
+ const MIN_CHUNK_SIZE : usize = 8192 ;
150
+
151
+ /// Build raw programs and divide them into chunks for parallelization
152
+ fn build_raw_program_chunks (
153
+ project_config : & ProjectConfig ,
154
+ project_asts : ProjectAsts ,
155
+ schema : Arc < SDLSchema > ,
156
+ log_event : & impl PerfLogEvent ,
157
+ build_mode : BuildMode ,
158
+ ) -> Result < ( Vec < Program > , SourceHashes ) , BuildProjectError > {
159
+ // Build a type aware IR.
160
+ let BuildIRResult { ir, source_hashes } = log_event. time ( "build_ir_time" , || {
161
+ build_ir:: build_ir ( project_config, project_asts, & schema, build_mode, log_event) . map_err (
162
+ |errors| BuildProjectError :: ValidationErrors {
163
+ errors,
164
+ project_name : project_config. name ,
165
+ } ,
166
+ )
167
+ } ) ?;
168
+
169
+ let chunks = if ir. len ( ) < MIN_CHUNK_SIZE {
170
+ vec ! [ ir]
171
+ } else {
172
+ let chunkify_time = log_event. start ( "chunkify_project_time" ) ;
173
+ let dependency_map = get_ir_definition_references ( & schema, & ir) ;
174
+ let definition_indexes: IndexSet < ExecutableDefinitionName > = ir
175
+ . iter ( )
176
+ . map ( |def| match def {
177
+ ExecutableDefinition :: Operation ( operation) => {
178
+ ExecutableDefinitionName :: OperationDefinitionName ( operation. name . item )
179
+ }
180
+ ExecutableDefinition :: Fragment ( fragment) => {
181
+ ExecutableDefinitionName :: FragmentDefinitionName ( fragment. name . item )
182
+ }
183
+ } )
184
+ . collect ( ) ;
185
+
186
+ let mut unionfind = UnionFind :: < usize > :: new ( definition_indexes. len ( ) ) ;
187
+ for ( source, destinations) in & dependency_map {
188
+ let source_index = definition_indexes. get_index_of ( source) . unwrap ( ) ;
189
+ for destination in destinations {
190
+ let destination_index = definition_indexes. get_index_of ( destination) . unwrap ( ) ;
191
+ unionfind. union ( source_index, destination_index) ;
192
+ }
193
+ }
194
+
195
+ let mut groups = FxHashMap :: default ( ) ;
196
+ for ( idx, def) in ir. into_iter ( ) . enumerate ( ) {
197
+ let group = unionfind. find ( idx) ;
198
+ groups. entry ( group) . or_insert_with ( Vec :: new) . push ( def) ;
199
+ }
200
+
201
+ let mut chunks = vec ! [ ] ;
202
+ let mut buffer = Vec :: new ( ) ;
203
+ for group in groups. into_values ( ) {
204
+ if group. len ( ) > MIN_CHUNK_SIZE {
205
+ chunks. push ( group) ;
206
+ } else {
207
+ buffer. extend ( group) ;
208
+ if buffer. len ( ) > MIN_CHUNK_SIZE {
209
+ chunks. push ( std:: mem:: take ( & mut buffer) ) ;
210
+ }
211
+ }
212
+ }
213
+ if !buffer. is_empty ( ) {
214
+ chunks. push ( buffer) ;
215
+ }
216
+ log_event. stop ( chunkify_time) ;
217
+ chunks
218
+ } ;
219
+
220
+ // Turn the IR into base Programs.
221
+ let programs = log_event. time ( "build_program_time" , || {
222
+ chunks
223
+ . into_iter ( )
224
+ . map ( |definitions| Program :: from_definitions ( Arc :: clone ( & schema) , definitions) )
225
+ . collect ( )
226
+ } ) ;
227
+ Ok ( ( programs, source_hashes) )
228
+ }
229
+
144
230
pub fn validate_program (
145
231
config : & Config ,
146
232
project_config : & ProjectConfig ,
@@ -270,26 +356,43 @@ pub fn build_programs(
270
356
}
271
357
} ,
272
358
) ;
273
- let ( program , source_hashes) =
274
- build_raw_program ( project_config, project_asts, schema, log_event, build_mode) ?;
359
+ let ( programs , source_hashes) =
360
+ build_raw_program_chunks ( project_config, project_asts, schema, log_event, build_mode) ?;
275
361
276
362
if compiler_state. should_cancel_current_build ( ) {
277
363
debug ! ( "Build is cancelled: updates in source code/or new file changes are pending." ) ;
278
364
return Err ( BuildProjectFailure :: Cancelled ) ;
279
365
}
366
+ let base_fragment_names = Arc :: new ( base_fragment_names) ;
367
+ let results: Vec < ( Programs , Vec < Diagnostic > ) > = programs
368
+ . into_par_iter ( )
369
+ . map ( |program| {
370
+ // Call validation rules that go beyond type checking.
371
+ // FIXME: Return non-fatal diagnostics from transforms (only validations for now)
372
+ let diagnostics = validate_program ( config, project_config, & program, log_event) ?;
373
+
374
+ let programs = transform_program (
375
+ project_config,
376
+ Arc :: new ( program) ,
377
+ Arc :: clone ( & base_fragment_names) ,
378
+ Arc :: clone ( & perf_logger) ,
379
+ log_event,
380
+ config. custom_transforms . as_ref ( ) ,
381
+ ) ?;
280
382
281
- // Call validation rules that go beyond type checking.
282
- // FIXME: Return non-fatal diagnostics from transforms (only validations for now)
283
- let diagnostics = validate_program ( config, project_config, & program, log_event) ?;
284
-
285
- let programs = transform_program (
286
- project_config,
287
- Arc :: new ( program) ,
288
- Arc :: new ( base_fragment_names) ,
289
- Arc :: clone ( & perf_logger) ,
290
- log_event,
291
- config. custom_transforms . as_ref ( ) ,
292
- ) ?;
383
+ Ok ( ( programs, diagnostics) )
384
+ } )
385
+ . collect :: < Result < Vec < _ > , BuildProjectFailure > > ( ) ?;
386
+
387
+ let len = results. len ( ) ;
388
+ let ( programs, diagnostics) = results. into_iter ( ) . fold (
389
+ ( Vec :: with_capacity ( len) , vec ! [ ] ) ,
390
+ |( mut programs, mut diagnostics) , ( temp_programs, temp_diagnostics) | {
391
+ programs. push ( temp_programs) ;
392
+ diagnostics. extend ( temp_diagnostics) ;
393
+ ( programs, diagnostics)
394
+ } ,
395
+ ) ;
293
396
294
397
Ok ( WithDiagnostics {
295
398
item : ( programs, Arc :: new ( source_hashes) ) ,
@@ -360,9 +463,19 @@ pub fn build_project(
360
463
361
464
// Generate artifacts by collecting information from the `Programs`.
362
465
let artifacts_timer = log_event. start ( "generate_artifacts_time" ) ;
363
- let artifacts = generate_artifacts ( project_config, & programs, Arc :: clone ( & source_hashes) ) ;
466
+ let artifacts = programs
467
+ . par_iter ( )
468
+ . map ( |programs| generate_artifacts ( project_config, programs, Arc :: clone ( & source_hashes) ) )
469
+ . flatten ( )
470
+ . collect ( ) ;
364
471
log_event. stop ( artifacts_timer) ;
365
472
473
+ let mut iter: std:: vec:: IntoIter < Programs > = programs. into_iter ( ) ;
474
+ let mut programs = iter. next ( ) . expect ( "Expect at least one result" ) ;
475
+ for temp_programs in iter {
476
+ merge_programs ( & mut programs, temp_programs) ;
477
+ }
478
+
366
479
log_event. number (
367
480
"generated_artifacts" ,
368
481
programs. reader . document_count ( ) + programs. normalization . document_count ( ) ,
@@ -376,6 +489,26 @@ pub fn build_project(
376
489
} )
377
490
}
378
491
492
+ fn merge_programs ( onto : & mut Programs , from : Programs ) {
493
+ merge_program ( Arc :: get_mut ( & mut onto. source ) . unwrap ( ) , from. source ) ;
494
+ merge_program ( Arc :: get_mut ( & mut onto. reader ) . unwrap ( ) , from. reader ) ;
495
+ merge_program (
496
+ Arc :: get_mut ( & mut onto. normalization ) . unwrap ( ) ,
497
+ from. normalization ,
498
+ ) ;
499
+ merge_program (
500
+ Arc :: get_mut ( & mut onto. operation_text ) . unwrap ( ) ,
501
+ from. operation_text ,
502
+ ) ;
503
+ merge_program ( Arc :: get_mut ( & mut onto. typegen ) . unwrap ( ) , from. typegen ) ;
504
+ }
505
+
506
+ fn merge_program ( onto : & mut Program , from : Arc < Program > ) {
507
+ let from = Arc :: unwrap_or_clone ( from) ;
508
+ onto. fragments . extend ( from. fragments ) ;
509
+ onto. operations . extend ( from. operations ) ;
510
+ }
511
+
379
512
#[ allow( clippy:: too_many_arguments) ]
380
513
pub async fn commit_project (
381
514
config : & Config ,
0 commit comments