@@ -37,18 +37,23 @@ use common::PerfLogger;
3737use common:: WithDiagnostics ;
3838use dashmap:: mapref:: entry:: Entry ;
3939use dashmap:: DashSet ;
40+ use dependency_analyzer:: get_ir_definition_references;
4041use fnv:: FnvBuildHasher ;
4142use fnv:: FnvHashMap ;
4243use fnv:: FnvHashSet ;
4344pub use generate_artifacts:: generate_artifacts;
4445pub use generate_artifacts:: generate_preloadable_query_parameters_artifact;
4546pub use generate_artifacts:: Artifact ;
4647pub use generate_artifacts:: ArtifactContent ;
48+ use graphql_ir:: ExecutableDefinition ;
49+ use graphql_ir:: ExecutableDefinitionName ;
4750use graphql_ir:: FragmentDefinitionNameSet ;
4851use graphql_ir:: Program ;
52+ use indexmap:: IndexSet ;
4953use log:: debug;
5054use log:: info;
5155use log:: warn;
56+ use petgraph:: unionfind:: UnionFind ;
5257use rayon:: iter:: IntoParallelRefIterator ;
5358use rayon:: slice:: ParallelSlice ;
5459use relay_codegen:: Printer ;
@@ -83,7 +88,7 @@ use crate::file_source::SourceControlUpdateStatus;
8388use crate :: graphql_asts:: GraphQLAsts ;
8489
8590type BuildProjectOutput = WithDiagnostics < ( ProjectName , Arc < SDLSchema > , Programs , Vec < Artifact > ) > ;
86- type BuildProgramsOutput = WithDiagnostics < ( Programs , Arc < SourceHashes > ) > ;
91+ type BuildProgramsOutput = WithDiagnostics < ( Vec < Programs > , Arc < SourceHashes > ) > ;
8792
8893pub enum BuildProjectFailure {
8994 Error ( BuildProjectError ) ,
@@ -141,6 +146,87 @@ pub fn build_raw_program(
141146 Ok ( ( program, source_hashes) )
142147}
143148
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+
144230pub fn validate_program (
145231 config : & Config ,
146232 project_config : & ProjectConfig ,
@@ -270,26 +356,43 @@ pub fn build_programs(
270356 }
271357 } ,
272358 ) ;
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) ?;
275361
276362 if compiler_state. should_cancel_current_build ( ) {
277363 debug ! ( "Build is cancelled: updates in source code/or new file changes are pending." ) ;
278364 return Err ( BuildProjectFailure :: Cancelled ) ;
279365 }
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+ ) ?;
280382
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+ ) ;
293396
294397 Ok ( WithDiagnostics {
295398 item : ( programs, Arc :: new ( source_hashes) ) ,
@@ -360,9 +463,19 @@ pub fn build_project(
360463
361464 // Generate artifacts by collecting information from the `Programs`.
362465 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 ( ) ;
364471 log_event. stop ( artifacts_timer) ;
365472
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+
366479 log_event. number (
367480 "generated_artifacts" ,
368481 programs. reader . document_count ( ) + programs. normalization . document_count ( ) ,
@@ -376,6 +489,26 @@ pub fn build_project(
376489 } )
377490}
378491
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+
379512#[ allow( clippy:: too_many_arguments) ]
380513pub async fn commit_project (
381514 config : & Config ,
0 commit comments