Skip to content

Commit d514263

Browse files
committed
Auto merge of #44853 - alexcrichton:debug-codegen-units, r=michaelwoerister
rustc: Default 32 codegen units at O0 This commit changes the default of rustc to use 32 codegen units when compiling in debug mode, typically an opt-level=0 compilation. Since their inception codegen units have matured quite a bit, gaining features such as: * Parallel translation and codegen enabling codegen units to get worked on even more quickly. * Deterministic and reliable partitioning through the same infrastructure as incremental compilation. * Global rate limiting through the `jobserver` crate to avoid overloading the system. The largest benefit of codegen units has forever been faster compilation through parallel processing of modules on the LLVM side of things, using all the cores available on build machines that typically have many available. Some downsides have been fixed through the features above, but the major downside remaining is that using codegen units reduces opportunities for inlining and optimization. This, however, doesn't matter much during debug builds! In this commit the default number of codegen units for debug builds has been raised from 1 to 32. This should enable most `cargo build` compiles that are bottlenecked on translation and/or code generation to immediately see speedups through parallelization on available cores. Work is being done to *always* enable multiple codegen units (and therefore parallel codegen) but it requires #44841 at least to be landed and stabilized, but stay tuned if you're interested in that aspect!
2 parents 0253d98 + 9e35b79 commit d514263

File tree

6 files changed

+97
-48
lines changed

6 files changed

+97
-48
lines changed

src/librustc/session/config.rs

+69-28
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,9 @@ top_level_options!(
350350
// is currently just a hack and will be removed eventually, so please
351351
// try to not rely on this too much.
352352
actually_rustdoc: bool [TRACKED],
353+
354+
// Number of object files/codegen units to produce on the backend
355+
codegen_units: usize [UNTRACKED],
353356
}
354357
);
355358

@@ -512,6 +515,7 @@ pub fn basic_options() -> Options {
512515
unstable_features: UnstableFeatures::Disallow,
513516
debug_assertions: true,
514517
actually_rustdoc: false,
518+
codegen_units: 1,
515519
}
516520
}
517521

@@ -529,11 +533,6 @@ impl Options {
529533
(self.debugging_opts.query_dep_graph || self.debugging_opts.incremental_info)
530534
}
531535

532-
pub fn single_codegen_unit(&self) -> bool {
533-
self.incremental.is_none() ||
534-
self.cg.codegen_units == 1
535-
}
536-
537536
pub fn file_path_mapping(&self) -> FilePathMapping {
538537
FilePathMapping::new(
539538
self.debugging_opts.remap_path_prefix_from.iter().zip(
@@ -791,7 +790,7 @@ macro_rules! options {
791790
fn parse_opt_uint(slot: &mut Option<usize>, v: Option<&str>) -> bool {
792791
match v {
793792
Some(s) => { *slot = s.parse().ok(); slot.is_some() }
794-
None => { *slot = None; true }
793+
None => { *slot = None; false }
795794
}
796795
}
797796

@@ -924,7 +923,7 @@ options! {CodegenOptions, CodegenSetter, basic_codegen_options,
924923
"metadata to mangle symbol names with"),
925924
extra_filename: String = ("".to_string(), parse_string, [UNTRACKED],
926925
"extra data to put in each output filename"),
927-
codegen_units: usize = (1, parse_uint, [UNTRACKED],
926+
codegen_units: Option<usize> = (None, parse_opt_uint, [UNTRACKED],
928927
"divide crate into N units to optimize in parallel"),
929928
remark: Passes = (SomePasses(Vec::new()), parse_passes, [UNTRACKED],
930929
"print remarks for these optimization passes (space separated, or \"all\")"),
@@ -1521,27 +1520,35 @@ pub fn build_session_options_and_crate_config(matches: &getopts::Matches)
15211520
}
15221521

15231522
let mut cg = build_codegen_options(matches, error_format);
1523+
let mut codegen_units = cg.codegen_units;
15241524

15251525
// Issue #30063: if user requests llvm-related output to one
15261526
// particular path, disable codegen-units.
1527-
if matches.opt_present("o") && cg.codegen_units != 1 {
1528-
let incompatible: Vec<_> = output_types.iter()
1529-
.map(|ot_path| ot_path.0)
1530-
.filter(|ot| {
1531-
!ot.is_compatible_with_codegen_units_and_single_output_file()
1532-
}).collect();
1533-
if !incompatible.is_empty() {
1534-
for ot in &incompatible {
1535-
early_warn(error_format, &format!("--emit={} with -o incompatible with \
1536-
-C codegen-units=N for N > 1",
1537-
ot.shorthand()));
1527+
let incompatible: Vec<_> = output_types.iter()
1528+
.map(|ot_path| ot_path.0)
1529+
.filter(|ot| {
1530+
!ot.is_compatible_with_codegen_units_and_single_output_file()
1531+
})
1532+
.map(|ot| ot.shorthand())
1533+
.collect();
1534+
if !incompatible.is_empty() {
1535+
match codegen_units {
1536+
Some(n) if n > 1 => {
1537+
if matches.opt_present("o") {
1538+
for ot in &incompatible {
1539+
early_warn(error_format, &format!("--emit={} with -o incompatible with \
1540+
-C codegen-units=N for N > 1",
1541+
ot));
1542+
}
1543+
early_warn(error_format, "resetting to default -C codegen-units=1");
1544+
codegen_units = Some(1);
1545+
}
15381546
}
1539-
early_warn(error_format, "resetting to default -C codegen-units=1");
1540-
cg.codegen_units = 1;
1547+
_ => codegen_units = Some(1),
15411548
}
15421549
}
15431550

1544-
if cg.codegen_units < 1 {
1551+
if codegen_units == Some(0) {
15451552
early_error(error_format, "Value for codegen units must be a positive nonzero integer");
15461553
}
15471554

@@ -1550,12 +1557,17 @@ pub fn build_session_options_and_crate_config(matches: &getopts::Matches)
15501557
// case, but it would be confusing to have the validity of
15511558
// `-Z lto -C codegen-units=2` depend on details of the crate being
15521559
// compiled, so we complain regardless.
1553-
if cg.lto && cg.codegen_units > 1 {
1554-
// This case is impossible to handle because LTO expects to be able
1555-
// to combine the entire crate and all its dependencies into a
1556-
// single compilation unit, but each codegen unit is in a separate
1557-
// LLVM context, so they can't easily be combined.
1558-
early_error(error_format, "can't perform LTO when using multiple codegen units");
1560+
if cg.lto {
1561+
if let Some(n) = codegen_units {
1562+
if n > 1 {
1563+
// This case is impossible to handle because LTO expects to be able
1564+
// to combine the entire crate and all its dependencies into a
1565+
// single compilation unit, but each codegen unit is in a separate
1566+
// LLVM context, so they can't easily be combined.
1567+
early_error(error_format, "can't perform LTO when using multiple codegen units");
1568+
}
1569+
}
1570+
codegen_units = Some(1);
15591571
}
15601572

15611573
if cg.lto && debugging_opts.incremental.is_some() {
@@ -1720,6 +1732,34 @@ pub fn build_session_options_and_crate_config(matches: &getopts::Matches)
17201732

17211733
let incremental = debugging_opts.incremental.as_ref().map(|m| PathBuf::from(m));
17221734

1735+
let codegen_units = codegen_units.unwrap_or_else(|| {
1736+
match opt_level {
1737+
// If we're compiling at `-O0` then default to 32 codegen units.
1738+
// The number here shouldn't matter too too much as debug mode
1739+
// builds don't rely on performance at all, meaning that lost
1740+
// opportunities for inlining through multiple codegen units is
1741+
// a non-issue.
1742+
//
1743+
// Note that the high number here doesn't mean that we'll be
1744+
// spawning a large number of threads in parallel. The backend
1745+
// of rustc contains global rate limiting through the
1746+
// `jobserver` crate so we'll never overload the system with too
1747+
// much work, but rather we'll only be optimizing when we're
1748+
// otherwise cooperating with other instances of rustc.
1749+
//
1750+
// Rather the high number here means that we should be able to
1751+
// keep a lot of idle cpus busy. By ensuring that no codegen
1752+
// unit takes *too* long to build we'll be guaranteed that all
1753+
// cpus will finish pretty closely to one another and we should
1754+
// make relatively optimal use of system resources
1755+
OptLevel::No => 32,
1756+
1757+
// All other optimization levels default use one codegen unit,
1758+
// the historical default in Rust for a Long Time.
1759+
_ => 1,
1760+
}
1761+
});
1762+
17231763
(Options {
17241764
crate_types,
17251765
optimize: opt_level,
@@ -1744,6 +1784,7 @@ pub fn build_session_options_and_crate_config(matches: &getopts::Matches)
17441784
unstable_features: UnstableFeatures::from_environment(),
17451785
debug_assertions,
17461786
actually_rustdoc: false,
1787+
codegen_units,
17471788
},
17481789
cfg)
17491790
}
@@ -2447,7 +2488,7 @@ mod tests {
24472488
opts.cg.extra_filename = String::from("extra-filename");
24482489
assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash());
24492490

2450-
opts.cg.codegen_units = 42;
2491+
opts.cg.codegen_units = Some(42);
24512492
assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash());
24522493

24532494
opts.cg.remark = super::SomePasses(vec![String::from("pass1"),

src/librustc_trans/back/link.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -509,7 +509,7 @@ fn link_rlib<'a>(sess: &'a Session,
509509
// of when we do and don't keep .#module-name#.bc files around.
510510
let user_wants_numbered_bitcode =
511511
sess.opts.output_types.contains_key(&OutputType::Bitcode) &&
512-
sess.opts.cg.codegen_units > 1;
512+
sess.opts.codegen_units > 1;
513513
if !sess.opts.cg.save_temps && !user_wants_numbered_bitcode {
514514
remove(sess, &bc_filename);
515515
}

src/librustc_trans/back/write.rs

+8-3
Original file line numberDiff line numberDiff line change
@@ -939,10 +939,10 @@ fn produce_final_output_artifacts(sess: &Session,
939939
let needs_crate_object = crate_output.outputs.contains_key(&OutputType::Exe);
940940

941941
let keep_numbered_bitcode = needs_crate_bitcode ||
942-
(user_wants_bitcode && sess.opts.cg.codegen_units > 1);
942+
(user_wants_bitcode && sess.opts.codegen_units > 1);
943943

944944
let keep_numbered_objects = needs_crate_object ||
945-
(user_wants_objects && sess.opts.cg.codegen_units > 1);
945+
(user_wants_objects && sess.opts.codegen_units > 1);
946946

947947
for module in compiled_modules.modules.iter() {
948948
let module_name = Some(&module.name[..]);
@@ -1520,6 +1520,11 @@ fn start_executing_work(tcx: TyCtxt,
15201520
total_llvm_time);
15211521
}
15221522

1523+
// Regardless of what order these modules completed in, report them to
1524+
// the backend in the same order every time to ensure that we're handing
1525+
// out deterministic results.
1526+
compiled_modules.sort_by(|a, b| a.name.cmp(&b.name));
1527+
15231528
let compiled_metadata_module = compiled_metadata_module
15241529
.expect("Metadata module not compiled?");
15251530

@@ -1853,7 +1858,7 @@ impl OngoingCrateTranslation {
18531858

18541859
// FIXME: time_llvm_passes support - does this use a global context or
18551860
// something?
1856-
if sess.opts.cg.codegen_units == 1 && sess.time_llvm_passes() {
1861+
if sess.opts.codegen_units == 1 && sess.time_llvm_passes() {
18571862
unsafe { llvm::LLVMRustPrintPassTimings(); }
18581863
}
18591864

src/librustc_trans/base.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1162,7 +1162,7 @@ fn collect_and_partition_translation_items<'a, 'tcx>(
11621162
let strategy = if tcx.sess.opts.debugging_opts.incremental.is_some() {
11631163
PartitioningStrategy::PerModule
11641164
} else {
1165-
PartitioningStrategy::FixedUnitCount(tcx.sess.opts.cg.codegen_units)
1165+
PartitioningStrategy::FixedUnitCount(tcx.sess.opts.codegen_units)
11661166
};
11671167

11681168
let codegen_units = time(time_passes, "codegen unit partitioning", || {
@@ -1175,7 +1175,7 @@ fn collect_and_partition_translation_items<'a, 'tcx>(
11751175
.collect::<Vec<_>>()
11761176
});
11771177

1178-
assert!(tcx.sess.opts.cg.codegen_units == codegen_units.len() ||
1178+
assert!(tcx.sess.opts.codegen_units == codegen_units.len() ||
11791179
tcx.sess.opts.debugging_opts.incremental.is_some());
11801180

11811181
let translation_items: DefIdSet = items.iter().filter_map(|trans_item| {

src/test/run-make/codegen-options-parsing/Makefile

+16-14
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,28 @@
11
-include ../tools.mk
22

3+
LOG = $(TMPDIR)/log.txt
4+
35
all:
46
#Option taking a number
5-
$(RUSTC) -C codegen-units dummy.rs 2>&1 | \
6-
grep 'codegen option `codegen-units` requires a number'
7-
$(RUSTC) -C codegen-units= dummy.rs 2>&1 | \
8-
grep 'incorrect value `` for codegen option `codegen-units` - a number was expected'
9-
$(RUSTC) -C codegen-units=foo dummy.rs 2>&1 | \
10-
grep 'incorrect value `foo` for codegen option `codegen-units` - a number was expected'
7+
$(RUSTC) -C codegen-units dummy.rs 2>&1 | tee $(LOG)
8+
grep 'codegen option `codegen-units` requires a number' $(LOG)
9+
$(RUSTC) -C codegen-units= dummy.rs 2>&1 | tee $(LOG)
10+
grep 'incorrect value `` for codegen option `codegen-units` - a number was expected' $(LOG)
11+
$(RUSTC) -C codegen-units=foo dummy.rs 2>&1 | tee $(LOG)
12+
grep 'incorrect value `foo` for codegen option `codegen-units` - a number was expected' $(LOG)
1113
$(RUSTC) -C codegen-units=1 dummy.rs
1214
#Option taking a string
13-
$(RUSTC) -C extra-filename dummy.rs 2>&1 | \
14-
grep 'codegen option `extra-filename` requires a string'
15+
$(RUSTC) -C extra-filename dummy.rs 2>&1 | tee $(LOG)
16+
grep 'codegen option `extra-filename` requires a string' $(LOG)
1517
$(RUSTC) -C extra-filename= dummy.rs 2>&1
1618
$(RUSTC) -C extra-filename=foo dummy.rs 2>&1
1719
#Option taking no argument
18-
$(RUSTC) -C lto= dummy.rs 2>&1 | \
19-
grep 'codegen option `lto` takes no value'
20-
$(RUSTC) -C lto=1 dummy.rs 2>&1 | \
21-
grep 'codegen option `lto` takes no value'
22-
$(RUSTC) -C lto=foo dummy.rs 2>&1 | \
23-
grep 'codegen option `lto` takes no value'
20+
$(RUSTC) -C lto= dummy.rs 2>&1 | tee $(LOG)
21+
grep 'codegen option `lto` takes no value' $(LOG)
22+
$(RUSTC) -C lto=1 dummy.rs 2>&1 | tee $(LOG)
23+
grep 'codegen option `lto` takes no value' $(LOG)
24+
$(RUSTC) -C lto=foo dummy.rs 2>&1 | tee $(LOG)
25+
grep 'codegen option `lto` takes no value' $(LOG)
2426
$(RUSTC) -C lto dummy.rs
2527

2628
# Should not link dead code...

src/test/run-make/llvm-phase/test.rs

+1
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ fn main() {
7777
.split(' ').map(|s| s.to_string()).collect();
7878
args.push("--out-dir".to_string());
7979
args.push(env::var("TMPDIR").unwrap());
80+
args.push("-Ccodegen-units=1".to_string());
8081

8182
let (result, _) = rustc_driver::run_compiler(
8283
&args, &mut JitCalls, Some(box JitLoader), None);

0 commit comments

Comments
 (0)