Skip to content

Commit 8e33f21

Browse files
committed
Inline CurrentDepGraph.
1 parent a82dc1d commit 8e33f21

File tree

1 file changed

+88
-134
lines changed
  • compiler/rustc_query_system/src/dep_graph

1 file changed

+88
-134
lines changed

compiler/rustc_query_system/src/dep_graph/graph.rs

Lines changed: 88 additions & 134 deletions
Original file line numberDiff line numberDiff line change
@@ -43,16 +43,33 @@ impl std::convert::From<DepNodeIndex> for QueryInvocationId {
4343
}
4444

4545
struct DepGraphData<K: DepKind> {
46-
/// The new encoding of the dependency graph, optimized for red/green
47-
/// tracking. The `current` field is the dependency graph of only the
48-
/// current compilation session: We don't merge the previous dep-graph into
49-
/// current one anymore, but we do reference shared data to save space.
50-
current: CurrentDepGraph,
51-
5246
/// The dep-graph from the previous compilation session. It contains all
5347
/// nodes and edges as well as all fingerprints of nodes that have them.
5448
previous: RwLock<SerializedDepGraph<K>>,
5549

50+
/// Used to trap when a specific edge is added to the graph.
51+
/// This is used for debug purposes and is only active with `debug_assertions`.
52+
#[allow(dead_code)]
53+
forbidden_edge: Option<EdgeFilter>,
54+
55+
/// Anonymous `DepNode`s are nodes whose IDs we compute from the list of
56+
/// their edges. This has the beneficial side-effect that multiple anonymous
57+
/// nodes can be coalesced into one without changing the semantics of the
58+
/// dependency graph. However, the merging of nodes can lead to a subtle
59+
/// problem during red-green marking: The color of an anonymous node from
60+
/// the current session might "shadow" the color of the node with the same
61+
/// ID from the previous session. In order to side-step this problem, we make
62+
/// sure that anonymous `NodeId`s allocated in different sessions don't overlap.
63+
/// This is implemented by mixing a session-key into the ID fingerprint of
64+
/// each anon node. The session-key is just a random number generated when
65+
/// the `DepGraph` is created.
66+
anon_id_seed: Fingerprint,
67+
68+
/// These are simple counters that are for profiling and
69+
/// debugging and only active with `debug_assertions`.
70+
total_read_count: AtomicU64,
71+
total_duplicate_read_count: AtomicU64,
72+
5673
/// A set of loaded diagnostics that is in the progress of being emitted.
5774
emitting_diagnostics: Lock<FxHashSet<DepNodeIndex>>,
5875

@@ -80,13 +97,70 @@ impl<K: DepKind> DepGraph<K> {
8097
prev_graph: SerializedDepGraph<K>,
8198
prev_work_products: FxHashMap<WorkProductId, WorkProduct>,
8299
) -> DepGraph<K> {
83-
let prev_graph_node_count = prev_graph.serialized_node_count();
100+
let _prev_graph_node_count = prev_graph.serialized_node_count();
101+
102+
use std::time::{SystemTime, UNIX_EPOCH};
103+
104+
let duration = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
105+
let nanos = duration.as_secs() * 1_000_000_000 + duration.subsec_nanos() as u64;
106+
let mut stable_hasher = StableHasher::new();
107+
nanos.hash(&mut stable_hasher);
108+
109+
let forbidden_edge = if cfg!(debug_assertions) {
110+
match env::var("RUST_FORBID_DEP_GRAPH_EDGE") {
111+
Ok(s) => match EdgeFilter::new(&s) {
112+
Ok(f) => Some(f),
113+
Err(err) => panic!("RUST_FORBID_DEP_GRAPH_EDGE invalid: {}", err),
114+
},
115+
Err(_) => None,
116+
}
117+
} else {
118+
None
119+
};
120+
121+
/*
122+
// Pre-allocate the dep node structures. We over-allocate a little so
123+
// that we hopefully don't have to re-allocate during this compilation
124+
// session. The over-allocation for new nodes is 2% plus a small
125+
// constant to account for the fact that in very small crates 2% might
126+
// not be enough. The allocation for red and green node data doesn't
127+
// include a constant, as we don't want to allocate anything for these
128+
// structures during full incremental builds, where they aren't used.
129+
//
130+
// These estimates are based on the distribution of node and edge counts
131+
// seen in rustc-perf benchmarks, adjusted somewhat to account for the
132+
// fact that these benchmarks aren't perfectly representative.
133+
//
134+
// FIXME Use a collection type that doesn't copy node and edge data and
135+
// grow multiplicatively on reallocation. Without such a collection or
136+
// solution having the same effect, there is a performance hazard here
137+
// in both time and space, as growing these collections means copying a
138+
// large amount of data and doubling already large buffer capacities. A
139+
// solution for this will also mean that it's less important to get
140+
// these estimates right.
141+
let new_node_count_estimate = (prev_graph_node_count * 2) / 100 + 200;
142+
let red_node_count_estimate = (prev_graph_node_count * 3) / 100;
143+
let light_green_node_count_estimate = (prev_graph_node_count * 25) / 100;
144+
let total_node_count_estimate = prev_graph_node_count + new_node_count_estimate;
145+
146+
let average_edges_per_node_estimate = 6;
147+
let unshared_edge_count_estimate = average_edges_per_node_estimate
148+
* (new_node_count_estimate + red_node_count_estimate + light_green_node_count_estimate);
149+
*/
150+
151+
// We store a large collection of these in `prev_index_to_index` during
152+
// non-full incremental builds, and want to ensure that the element size
153+
// doesn't inadvertently increase.
154+
static_assert_size!(Option<DepNodeIndex>, 4);
84155

85156
DepGraph {
86157
data: Some(Lrc::new(DepGraphData {
87158
previous_work_products: prev_work_products,
88159
dep_node_debug: Default::default(),
89-
current: CurrentDepGraph::new(prev_graph_node_count),
160+
anon_id_seed: stable_hasher.finish(),
161+
forbidden_edge,
162+
total_read_count: AtomicU64::new(0),
163+
total_duplicate_read_count: AtomicU64::new(0),
90164
emitting_diagnostics: Default::default(),
91165
previous: RwLock::new(prev_graph),
92166
})),
@@ -239,7 +313,7 @@ impl<K: DepKind> DepGraph<K> {
239313
// Fingerprint::combine() is faster than sending Fingerprint
240314
// through the StableHasher (at least as long as StableHasher
241315
// is so slow).
242-
hash: data.current.anon_id_seed.combine(hasher.finish()).into(),
316+
hash: data.anon_id_seed.combine(hasher.finish()).into(),
243317
};
244318

245319
let mut previous = data.previous.write();
@@ -272,7 +346,7 @@ impl<K: DepKind> DepGraph<K> {
272346
let mut task_deps = task_deps.lock();
273347
let task_deps = &mut *task_deps;
274348
if cfg!(debug_assertions) {
275-
data.current.total_read_count.fetch_add(1, Relaxed);
349+
data.total_read_count.fetch_add(1, Relaxed);
276350
}
277351

278352
// As long as we only have a low number of reads we can avoid doing a hash
@@ -293,7 +367,7 @@ impl<K: DepKind> DepGraph<K> {
293367
#[cfg(debug_assertions)]
294368
{
295369
if let Some(target) = task_deps.node {
296-
if let Some(ref forbidden_edge) = data.current.forbidden_edge {
370+
if let Some(ref forbidden_edge) = data.forbidden_edge {
297371
let src = self.dep_node_of(dep_node_index);
298372
if forbidden_edge.test(&src, &target) {
299373
panic!("forbidden edge {:?} -> {:?} created", src, target)
@@ -302,7 +376,7 @@ impl<K: DepKind> DepGraph<K> {
302376
}
303377
}
304378
} else if cfg!(debug_assertions) {
305-
data.current.total_duplicate_read_count.fetch_add(1, Relaxed);
379+
data.total_duplicate_read_count.fetch_add(1, Relaxed);
306380
}
307381
}
308382
})
@@ -687,7 +761,6 @@ impl<K: DepKind> DepGraph<K> {
687761

688762
let data = self.data.as_ref().unwrap();
689763
let prev = &data.previous.read();
690-
let current = &data.current;
691764

692765
let mut stats: FxHashMap<_, Stat<K>> = FxHashMap::with_hasher(Default::default());
693766

@@ -721,8 +794,8 @@ impl<K: DepKind> DepGraph<K> {
721794
eprintln!("[incremental] Total Edge Count: {}", total_edge_count);
722795

723796
if cfg!(debug_assertions) {
724-
let total_edge_reads = current.total_read_count.load(Relaxed);
725-
let total_duplicate_edge_reads = current.total_duplicate_read_count.load(Relaxed);
797+
let total_edge_reads = data.total_read_count.load(Relaxed);
798+
let total_duplicate_edge_reads = data.total_duplicate_read_count.load(Relaxed);
726799

727800
eprintln!("[incremental] Total Edge Reads: {}", total_edge_reads);
728801
eprintln!("[incremental] Total Duplicate Edge Reads: {}", total_duplicate_edge_reads);
@@ -819,125 +892,6 @@ rustc_index::newtype_index! {
819892
struct EdgeIndex { .. }
820893
}
821894

822-
/// `CurrentDepGraph` stores the dependency graph for the current session. It
823-
/// will be populated as we run queries or tasks. We never remove nodes from the
824-
/// graph: they are only added.
825-
///
826-
/// The nodes in it are identified by a `DepNodeIndex`. Internally, this maps to
827-
/// a `HybridIndex`, which identifies which collection in the `data` field
828-
/// contains a node's data. Which collection is used for a node depends on
829-
/// whether the node was present in the `SerializedDepGraph`, and if so, the color
830-
/// of the node. Each type of node can share more or less data with the previous
831-
/// graph. When possible, we can store just the index of the node in the
832-
/// previous graph, rather than duplicating its data in our own collections.
833-
/// This is important, because these graph structures are some of the largest in
834-
/// the compiler.
835-
///
836-
/// For the same reason, we also avoid storing `DepNode`s more than once as map
837-
/// keys. The `new_node_to_index` map only contains nodes not in the previous
838-
/// graph, and we map nodes in the previous graph to indices via a two-step
839-
/// mapping. `SerializedDepGraph` maps from `DepNode` to `SerializedDepNodeIndex`,
840-
/// and the `prev_index_to_index` vector (which is more compact and faster than
841-
/// using a map) maps from `SerializedDepNodeIndex` to `DepNodeIndex`.
842-
///
843-
/// This struct uses three locks internally. The `data`, `new_node_to_index`,
844-
/// and `prev_index_to_index` fields are locked separately. Operations that take
845-
/// a `DepNodeIndex` typically just access the `data` field.
846-
///
847-
/// We only need to manipulate at most two locks simultaneously:
848-
/// `new_node_to_index` and `data`, or `prev_index_to_index` and `data`. When
849-
/// manipulating both, we acquire `new_node_to_index` or `prev_index_to_index`
850-
/// first, and `data` second.
851-
pub(super) struct CurrentDepGraph {
852-
/// Used to trap when a specific edge is added to the graph.
853-
/// This is used for debug purposes and is only active with `debug_assertions`.
854-
#[allow(dead_code)]
855-
forbidden_edge: Option<EdgeFilter>,
856-
857-
/// Anonymous `DepNode`s are nodes whose IDs we compute from the list of
858-
/// their edges. This has the beneficial side-effect that multiple anonymous
859-
/// nodes can be coalesced into one without changing the semantics of the
860-
/// dependency graph. However, the merging of nodes can lead to a subtle
861-
/// problem during red-green marking: The color of an anonymous node from
862-
/// the current session might "shadow" the color of the node with the same
863-
/// ID from the previous session. In order to side-step this problem, we make
864-
/// sure that anonymous `NodeId`s allocated in different sessions don't overlap.
865-
/// This is implemented by mixing a session-key into the ID fingerprint of
866-
/// each anon node. The session-key is just a random number generated when
867-
/// the `DepGraph` is created.
868-
anon_id_seed: Fingerprint,
869-
870-
/// These are simple counters that are for profiling and
871-
/// debugging and only active with `debug_assertions`.
872-
total_read_count: AtomicU64,
873-
total_duplicate_read_count: AtomicU64,
874-
}
875-
876-
impl CurrentDepGraph {
877-
fn new(_prev_graph_node_count: usize) -> CurrentDepGraph {
878-
use std::time::{SystemTime, UNIX_EPOCH};
879-
880-
let duration = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
881-
let nanos = duration.as_secs() * 1_000_000_000 + duration.subsec_nanos() as u64;
882-
let mut stable_hasher = StableHasher::new();
883-
nanos.hash(&mut stable_hasher);
884-
885-
let forbidden_edge = if cfg!(debug_assertions) {
886-
match env::var("RUST_FORBID_DEP_GRAPH_EDGE") {
887-
Ok(s) => match EdgeFilter::new(&s) {
888-
Ok(f) => Some(f),
889-
Err(err) => panic!("RUST_FORBID_DEP_GRAPH_EDGE invalid: {}", err),
890-
},
891-
Err(_) => None,
892-
}
893-
} else {
894-
None
895-
};
896-
897-
/*
898-
// Pre-allocate the dep node structures. We over-allocate a little so
899-
// that we hopefully don't have to re-allocate during this compilation
900-
// session. The over-allocation for new nodes is 2% plus a small
901-
// constant to account for the fact that in very small crates 2% might
902-
// not be enough. The allocation for red and green node data doesn't
903-
// include a constant, as we don't want to allocate anything for these
904-
// structures during full incremental builds, where they aren't used.
905-
//
906-
// These estimates are based on the distribution of node and edge counts
907-
// seen in rustc-perf benchmarks, adjusted somewhat to account for the
908-
// fact that these benchmarks aren't perfectly representative.
909-
//
910-
// FIXME Use a collection type that doesn't copy node and edge data and
911-
// grow multiplicatively on reallocation. Without such a collection or
912-
// solution having the same effect, there is a performance hazard here
913-
// in both time and space, as growing these collections means copying a
914-
// large amount of data and doubling already large buffer capacities. A
915-
// solution for this will also mean that it's less important to get
916-
// these estimates right.
917-
let new_node_count_estimate = (prev_graph_node_count * 2) / 100 + 200;
918-
let red_node_count_estimate = (prev_graph_node_count * 3) / 100;
919-
let light_green_node_count_estimate = (prev_graph_node_count * 25) / 100;
920-
let total_node_count_estimate = prev_graph_node_count + new_node_count_estimate;
921-
922-
let average_edges_per_node_estimate = 6;
923-
let unshared_edge_count_estimate = average_edges_per_node_estimate
924-
* (new_node_count_estimate + red_node_count_estimate + light_green_node_count_estimate);
925-
*/
926-
927-
// We store a large collection of these in `prev_index_to_index` during
928-
// non-full incremental builds, and want to ensure that the element size
929-
// doesn't inadvertently increase.
930-
static_assert_size!(Option<DepNodeIndex>, 4);
931-
932-
CurrentDepGraph {
933-
anon_id_seed: stable_hasher.finish(),
934-
forbidden_edge,
935-
total_read_count: AtomicU64::new(0),
936-
total_duplicate_read_count: AtomicU64::new(0),
937-
}
938-
}
939-
}
940-
941895
/// The capacity of the `reads` field `SmallVec`
942896
const TASK_DEPS_READS_CAP: usize = 8;
943897
type EdgesVec = SmallVec<[DepNodeIndex; TASK_DEPS_READS_CAP]>;

0 commit comments

Comments
 (0)