Skip to content

Commit 8447f4f

Browse files
committed
Add CGU size heuristic for partitioning
This addresses the concern of rust-lang#47316 by estimating CGU size based on the size of its MIR. Looking at the size estimate differences for a small selection of crates, this heuristic produces different orderings, which should more accurately reflect optimisation time. Fixes rust-lang#47316.
1 parent 3bd4af8 commit 8447f4f

File tree

1 file changed

+42
-4
lines changed

1 file changed

+42
-4
lines changed

src/librustc_mir/monomorphize/partitioning.rs

Lines changed: 42 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -110,11 +110,12 @@ use rustc::mir::mono::{Linkage, Visibility};
110110
use rustc::ty::{self, TyCtxt, InstanceDef};
111111
use rustc::ty::item_path::characteristic_def_id_of_type;
112112
use rustc::util::nodemap::{FxHashMap, FxHashSet};
113-
use std::collections::hash_map::Entry;
113+
use std::collections::hash_map::{HashMap, Entry};
114114
use syntax::ast::NodeId;
115115
use syntax::symbol::{Symbol, InternedString};
116116
use rustc::mir::mono::MonoItem;
117117
use monomorphize::item::{MonoItemExt, InstantiationMode};
118+
use core::usize;
118119

119120
pub use rustc::mir::mono::CodegenUnit;
120121

@@ -229,7 +230,7 @@ pub fn partition<'a, 'tcx, I>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
229230
// If the partitioning should produce a fixed count of codegen units, merge
230231
// until that count is reached.
231232
if let PartitioningStrategy::FixedUnitCount(count) = strategy {
232-
merge_codegen_units(&mut initial_partitioning, count, &tcx.crate_name.as_str());
233+
merge_codegen_units(tcx, &mut initial_partitioning, count, &tcx.crate_name.as_str());
233234

234235
debug_dump(tcx, "POST MERGING:", initial_partitioning.codegen_units.iter());
235236
}
@@ -404,7 +405,8 @@ fn place_root_translation_items<'a, 'tcx, I>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
404405
}
405406
}
406407

407-
fn merge_codegen_units<'tcx>(initial_partitioning: &mut PreInliningPartitioning<'tcx>,
408+
fn merge_codegen_units<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
409+
initial_partitioning: &mut PreInliningPartitioning<'tcx>,
408410
target_cgu_count: usize,
409411
crate_name: &str) {
410412
assert!(target_cgu_count >= 1);
@@ -421,12 +423,48 @@ fn merge_codegen_units<'tcx>(initial_partitioning: &mut PreInliningPartitioning<
421423
// the stable sort below will keep everything nice and deterministic.
422424
codegen_units.sort_by_key(|cgu| cgu.name().clone());
423425

426+
// Estimate the size of a codegen unit as (approximately) the number of MIR
427+
// statements it corresponds to.
428+
fn codegen_unit_size_estimate<'a, 'tcx>(cgu: &CodegenUnit<'tcx>,
429+
mono_item_sizes: &HashMap<MonoItem, usize>)
430+
-> usize {
431+
cgu.items().keys().map(|mi| mono_item_sizes.get(mi).unwrap()).sum()
432+
}
433+
434+
fn mono_item_size_estimate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
435+
item: &MonoItem<'tcx>)
436+
-> usize {
437+
match item {
438+
MonoItem::Fn(instance) => {
439+
// Estimate the size of a function based on how many statements
440+
// it contains.
441+
let mir = tcx.instance_mir(instance.def);
442+
mir.basic_blocks().iter().map(|bb| bb.statements.len()).sum()
443+
},
444+
// Conservatively estimate the size of a static declaration
445+
// or assembly to be 1.
446+
MonoItem::Static(_) | MonoItem::GlobalAsm(_) => 1,
447+
}
448+
}
449+
450+
// Since `sort_by_key` currently recomputes the keys for each comparison,
451+
// we can save unnecessary recomputations by storing size estimates for
452+
// each `MonoItem`. Storing estimates for `CodegenUnit` might be preferable,
453+
// but its structure makes it awkward to use as a key and additionally their
454+
// sizes change as the merging occurs, requiring the map to be updated.
455+
let mut sizes: HashMap<MonoItem, usize> = HashMap::new();
456+
for mis in codegen_units.iter().map(|cgu| cgu.items().keys()) {
457+
mis.for_each(|mi| {
458+
sizes.entry(*mi).or_insert_with(|| mono_item_size_estimate(tcx, mi));
459+
});
460+
}
461+
424462
// Merge the two smallest codegen units until the target size is reached.
425463
// Note that "size" is estimated here rather inaccurately as the number of
426464
// translation items in a given unit. This could be improved on.
427465
while codegen_units.len() > target_cgu_count {
428466
// Sort small cgus to the back
429-
codegen_units.sort_by_key(|cgu| -(cgu.items().len() as i64));
467+
codegen_units.sort_by_key(|cgu| usize::MAX - codegen_unit_size_estimate(cgu, &sizes));
430468
let mut smallest = codegen_units.pop().unwrap();
431469
let second_smallest = codegen_units.last_mut().unwrap();
432470

0 commit comments

Comments
 (0)