@@ -609,54 +609,75 @@ pub fn codegen_crate<B: ExtraBackendMethods>(
609609
610610 let total_codegen_time = Lock :: new ( Duration :: new ( 0 , 0 ) ) ;
611611
612- let cgu_reuse: Vec < _ > = tcx. sess . time ( "find cgu reuse" , || {
613- codegen_units. iter ( ) . map ( |cgu| determine_cgu_reuse ( tcx, & cgu) ) . collect ( )
614- } ) ;
615-
616- let mut cgus: FxHashMap < usize , _ > = if cfg ! ( parallel_compiler) {
617- tcx. sess . time ( "compile first CGUs" , || {
618- // Try to find one CGU to compile per thread.
619- let cgus: Vec < _ > = cgu_reuse
620- . iter ( )
621- . enumerate ( )
622- . filter ( |& ( _, reuse) | reuse == & CguReuse :: No )
623- . take ( tcx. sess . threads ( ) )
624- . collect ( ) ;
625-
626- // Compile the found CGUs in parallel.
627- par_iter ( cgus)
628- . map ( |( i, _) | {
629- let start_time = Instant :: now ( ) ;
630- let module = backend. compile_codegen_unit ( tcx, codegen_units[ i] . name ( ) ) ;
631- let mut time = total_codegen_time. lock ( ) ;
632- * time += start_time. elapsed ( ) ;
633- ( i, module)
634- } )
635- . collect ( )
636- } )
637- } else {
638- FxHashMap :: default ( )
612+ // The non-parallel compiler can only translate codegen units to LLVM IR
613+ // on a single thread, leading to a staircase effect where the N LLVM
614+ // threads have to wait on the single codegen threads to generate work
615+ // for them. The parallel compiler does not have this restriction, so
616+ // we can pre-load the LLVM queue in parallel before handing off
617+ // coordination to the OnGoingCodegen scheduler.
618+ //
619+ // This likely is a temporary measure. Once we don't have to support the
620+ // non-parallel compiler anymore, we can compile CGUs end-to-end in
621+ // parallel and get rid of the complicated scheduling logic.
622+ let pre_compile_cgus = |cgu_reuse : & [ CguReuse ] | {
623+ if cfg ! ( parallel_compiler) {
624+ tcx. sess . time ( "compile_first_CGU_batch" , || {
625+ // Try to find one CGU to compile per thread.
626+ let cgus: Vec < _ > = cgu_reuse
627+ . iter ( )
628+ . enumerate ( )
629+ . filter ( |& ( _, reuse) | reuse == & CguReuse :: No )
630+ . take ( tcx. sess . threads ( ) )
631+ . collect ( ) ;
632+
633+ // Compile the found CGUs in parallel.
634+ par_iter ( cgus)
635+ . map ( |( i, _) | {
636+ let start_time = Instant :: now ( ) ;
637+ let module = backend. compile_codegen_unit ( tcx, codegen_units[ i] . name ( ) ) ;
638+ let mut time = total_codegen_time. lock ( ) ;
639+ * time += start_time. elapsed ( ) ;
640+ ( i, module)
641+ } )
642+ . collect ( )
643+ } )
644+ } else {
645+ FxHashMap :: default ( )
646+ }
639647 } ;
640648
641- let mut total_codegen_time = total_codegen_time. into_inner ( ) ;
649+ let mut cgu_reuse = Vec :: new ( ) ;
650+ let mut pre_compiled_cgus: Option < FxHashMap < usize , _ > > = None ;
642651
643- for ( i, cgu) in codegen_units. into_iter ( ) . enumerate ( ) {
652+ for ( i, cgu) in codegen_units. iter ( ) . enumerate ( ) {
644653 ongoing_codegen. wait_for_signal_to_codegen_item ( ) ;
645654 ongoing_codegen. check_for_errors ( tcx. sess ) ;
646655
656+ // Do some setup work in the first iteration
657+ if pre_compiled_cgus. is_none ( ) {
658+ // Calculate the CGU reuse
659+ cgu_reuse = tcx. sess . time ( "find_cgu_reuse" , || {
660+ codegen_units. iter ( ) . map ( |cgu| determine_cgu_reuse ( tcx, & cgu) ) . collect ( )
661+ } ) ;
662+ // Pre compile some CGUs
663+ pre_compiled_cgus = Some ( pre_compile_cgus ( & cgu_reuse) ) ;
664+ }
665+
647666 let cgu_reuse = cgu_reuse[ i] ;
648667 tcx. sess . cgu_reuse_tracker . set_actual_reuse ( & cgu. name ( ) . as_str ( ) , cgu_reuse) ;
649668
650669 match cgu_reuse {
651670 CguReuse :: No => {
652- let ( module, cost) = if let Some ( cgu) = cgus. remove ( & i) {
653- cgu
654- } else {
655- let start_time = Instant :: now ( ) ;
656- let module = backend. compile_codegen_unit ( tcx, cgu. name ( ) ) ;
657- total_codegen_time += start_time. elapsed ( ) ;
658- module
659- } ;
671+ let ( module, cost) =
672+ if let Some ( cgu) = pre_compiled_cgus. as_mut ( ) . unwrap ( ) . remove ( & i) {
673+ cgu
674+ } else {
675+ let start_time = Instant :: now ( ) ;
676+ let module = backend. compile_codegen_unit ( tcx, cgu. name ( ) ) ;
677+ let mut time = total_codegen_time. lock ( ) ;
678+ * time += start_time. elapsed ( ) ;
679+ module
680+ } ;
660681 submit_codegened_module_to_llvm (
661682 & backend,
662683 & ongoing_codegen. coordinator_send ,
@@ -695,7 +716,11 @@ pub fn codegen_crate<B: ExtraBackendMethods>(
695716
696717 // Since the main thread is sometimes blocked during codegen, we keep track
697718 // -Ztime-passes output manually.
698- print_time_passes_entry ( tcx. sess . time_passes ( ) , "codegen_to_LLVM_IR" , total_codegen_time) ;
719+ print_time_passes_entry (
720+ tcx. sess . time_passes ( ) ,
721+ "codegen_to_LLVM_IR" ,
722+ total_codegen_time. into_inner ( ) ,
723+ ) ;
699724
700725 :: rustc_incremental:: assert_module_sources:: assert_module_sources ( tcx) ;
701726
0 commit comments