From ef9ae8581ff17dd88eb0260076bfe80d8d9c4aba Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 3 Feb 2017 06:08:16 -0500 Subject: [PATCH] make dirty process O(dirty) The old algorithm was O(graph) --- src/librustc_incremental/persist/data.rs | 17 +++-- src/librustc_incremental/persist/load.rs | 90 ++++++++++++++---------- src/librustc_incremental/persist/save.rs | 7 +- 3 files changed, 65 insertions(+), 49 deletions(-) diff --git a/src/librustc_incremental/persist/data.rs b/src/librustc_incremental/persist/data.rs index f0e4f4f99ef..60f24b71de2 100644 --- a/src/librustc_incremental/persist/data.rs +++ b/src/librustc_incremental/persist/data.rs @@ -21,7 +21,7 @@ use super::directory::DefPathIndex; /// Data for use when recompiling the **current crate**. #[derive(Debug, RustcEncodable, RustcDecodable)] pub struct SerializedDepGraph { - pub edges: Vec, + pub edges: Vec, /// These are hashes of two things: /// - the HIR nodes in this crate @@ -45,14 +45,13 @@ pub struct SerializedDepGraph { pub hashes: Vec, } -/// Represents a "reduced" dependency edge. Unlike the full dep-graph, -/// the dep-graph we serialize contains only edges `S -> T` where the -/// source `S` is something hashable (a HIR node or foreign metadata) -/// and the target `T` is something significant, like a work-product. -/// Normally, significant nodes are only those that have saved data on -/// disk, but in unit-testing the set of significant nodes can be -/// increased. -pub type SerializedEdge = (DepNode, DepNode); +/// Represents a set of "reduced" dependency edge. We group the +/// outgoing edges from a single source together. +#[derive(Debug, RustcEncodable, RustcDecodable)] +pub struct SerializedEdgeSet { + pub source: DepNode, + pub targets: Vec> +} #[derive(Debug, RustcEncodable, RustcDecodable)] pub struct SerializedHash { diff --git a/src/librustc_incremental/persist/load.rs b/src/librustc_incremental/persist/load.rs index e9a59fd7629..b371ab6aa31 100644 --- a/src/librustc_incremental/persist/load.rs +++ b/src/librustc_incremental/persist/load.rs @@ -152,6 +152,11 @@ pub fn decode_dep_graph<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, let directory = DefIdDirectory::decode(&mut dep_graph_decoder)?; let serialized_dep_graph = SerializedDepGraph::decode(&mut dep_graph_decoder)?; + let edge_map: FxHashMap<_, _> = serialized_dep_graph.edges + .into_iter() + .map(|s| (s.source, s.targets)) + .collect(); + // Retrace the paths in the directory to find their current location (if any). let retraced = directory.retrace(tcx); @@ -166,46 +171,48 @@ pub fn decode_dep_graph<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, incremental_hashes_map, &serialized_dep_graph.hashes, &retraced); - let dirty_raw_nodes = transitive_dirty_nodes(&serialized_dep_graph.edges, dirty_raw_nodes); + let dirty_raw_nodes = transitive_dirty_nodes(&edge_map, dirty_raw_nodes); // Recreate the edges in the graph that are still clean. let mut clean_work_products = FxHashSet(); let mut dirty_work_products = FxHashSet(); // incomplete; just used to suppress debug output - for edge in &serialized_dep_graph.edges { - // If the target is dirty, skip the edge. If this is an edge - // that targets a work-product, we can print the blame - // information now. - if let Some(blame) = dirty_raw_nodes.get(&edge.1) { - if let DepNode::WorkProduct(ref wp) = edge.1 { - if tcx.sess.opts.debugging_opts.incremental_info { - if dirty_work_products.insert(wp.clone()) { - // It'd be nice to pretty-print these paths better than just - // using the `Debug` impls, but wev. - println!("incremental: module {:?} is dirty because {:?} \ - changed or was removed", - wp, - blame.map_def(|&index| { - Some(directory.def_path_string(tcx, index)) - }).unwrap()); + for (source, targets) in &edge_map { + for target in targets { + // If the target is dirty, skip the edge. If this is an edge + // that targets a work-product, we can print the blame + // information now. + if let Some(blame) = dirty_raw_nodes.get(target) { + if let DepNode::WorkProduct(ref wp) = *target { + if tcx.sess.opts.debugging_opts.incremental_info { + if dirty_work_products.insert(wp.clone()) { + // It'd be nice to pretty-print these paths better than just + // using the `Debug` impls, but wev. + println!("incremental: module {:?} is dirty because {:?} \ + changed or was removed", + wp, + blame.map_def(|&index| { + Some(directory.def_path_string(tcx, index)) + }).unwrap()); + } } } + continue; } - continue; - } - // If the source is dirty, the target will be dirty. - assert!(!dirty_raw_nodes.contains_key(&edge.0)); + // If the source is dirty, the target will be dirty. + assert!(!dirty_raw_nodes.contains_key(source)); - // Retrace the source -> target edges to def-ids and then - // create an edge in the graph. Retracing may yield none if - // some of the data happens to have been removed; this ought - // to be impossible unless it is dirty, so we can unwrap. - let source_node = retraced.map(&edge.0).unwrap(); - let target_node = retraced.map(&edge.1).unwrap(); - let _task = tcx.dep_graph.in_task(target_node); - tcx.dep_graph.read(source_node); - if let DepNode::WorkProduct(ref wp) = edge.1 { - clean_work_products.insert(wp.clone()); + // Retrace the source -> target edges to def-ids and then + // create an edge in the graph. Retracing may yield none if + // some of the data happens to have been removed; this ought + // to be impossible unless it is dirty, so we can unwrap. + let source_node = retraced.map(source).unwrap(); + let target_node = retraced.map(target).unwrap(); + let _task = tcx.dep_graph.in_task(target_node); + tcx.dep_graph.read(source_node); + if let DepNode::WorkProduct(ref wp) = *target { + clean_work_products.insert(wp.clone()); + } } } @@ -268,16 +275,23 @@ fn initial_dirty_nodes<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, dirty_nodes } -fn transitive_dirty_nodes(edges: &[SerializedEdge], +fn transitive_dirty_nodes(edge_map: &FxHashMap, Vec>>, mut dirty_nodes: DirtyNodes) -> DirtyNodes { - let mut len = 0; - while len != dirty_nodes.len() { - len = dirty_nodes.len(); - for edge in edges { - if let Some(n) = dirty_nodes.get(&edge.0).cloned() { - dirty_nodes.insert(edge.1.clone(), n); + let mut stack: Vec<(DepNode, DepNode)> = vec![]; + stack.extend(dirty_nodes.iter().map(|(s, b)| (s.clone(), b.clone()))); + while let Some((source, blame)) = stack.pop() { + // we know the source is dirty (because of the node `blame`)... + assert!(dirty_nodes.contains_key(&source)); + + // ...so we dirty all the targets (with the same blame) + if let Some(targets) = edge_map.get(&source) { + for target in targets { + if !dirty_nodes.contains_key(target) { + dirty_nodes.insert(target.clone(), blame.clone()); + stack.push((target.clone(), blame.clone())); + } } } } diff --git a/src/librustc_incremental/persist/save.rs b/src/librustc_incremental/persist/save.rs index 80f51700d60..93600631b8d 100644 --- a/src/librustc_incremental/persist/save.rs +++ b/src/librustc_incremental/persist/save.rs @@ -178,7 +178,7 @@ pub fn encode_dep_graph(preds: &Predecessors, // Create a flat list of (Input, WorkProduct) edges for // serialization. - let mut edges = vec![]; + let mut edges = FxHashMap(); for edge in preds.reduced_graph.all_edges() { let source = *preds.reduced_graph.node_data(edge.source()); let target = *preds.reduced_graph.node_data(edge.target()); @@ -194,7 +194,7 @@ pub fn encode_dep_graph(preds: &Predecessors, debug!("serialize edge: {:?} -> {:?}", source, target); let source = builder.map(source); let target = builder.map(target); - edges.push((source, target)); + edges.entry(source).or_insert(vec![]).push(target); } if tcx.sess.opts.debugging_opts.incremental_dump_hash { @@ -204,6 +204,9 @@ pub fn encode_dep_graph(preds: &Predecessors, } // Create the serialized dep-graph. + let edges = edges.into_iter() + .map(|(k, v)| SerializedEdgeSet { source: k, targets: v }) + .collect(); let graph = SerializedDepGraph { edges: edges, hashes: preds.hashes