Skip to content

Commit 69bacd0

Browse files
committed
Precompile CGUs while the main thread has the implicit job server token
1 parent f45758c commit 69bacd0

File tree

2 files changed

+65
-38
lines changed

2 files changed

+65
-38
lines changed

src/librustc_codegen_ssa/base.rs

+63-38
Original file line numberDiff line numberDiff line change
@@ -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

src/librustc_codegen_ssa/traits/backend.rs

+2
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ pub trait ExtraBackendMethods: CodegenBackend + WriteBackendMethods + Sized + Se
4848
mods: &mut Self::Module,
4949
kind: AllocatorKind,
5050
);
51+
/// This generates the codegen unit and returns it along with
52+
/// a `u64` giving an estimate of the unit's processing cost.
5153
fn compile_codegen_unit(
5254
&self,
5355
tcx: TyCtxt<'_>,

0 commit comments

Comments
 (0)