Skip to content

Commit b5d46ec

Browse files
committed
Auto merge of #157781 - xmakro:perf/dep-graph-edge-reuse, r=cjgillot
perf: reuse green-marking's edge walk when promoting a node
2 parents 3daae5e + bd268e1 commit b5d46ec

2 files changed

Lines changed: 86 additions & 56 deletions

File tree

compiler/rustc_middle/src/dep_graph/graph.rs

Lines changed: 71 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use std::assert_matches;
2+
use std::cell::Cell;
23
use std::fmt::Debug;
34
use std::hash::Hash;
45
use std::sync::Arc;
@@ -9,7 +10,7 @@ use rustc_data_structures::fx::FxHashSet;
910
use rustc_data_structures::profiling::QueryInvocationId;
1011
use rustc_data_structures::sharded::{self, ShardedHashMap};
1112
use rustc_data_structures::stable_hash::{StableHash, StableHasher};
12-
use rustc_data_structures::sync::{AtomicU64, Lock};
13+
use rustc_data_structures::sync::{AtomicU64, Lock, WorkerLocal};
1314
use rustc_data_structures::unord::UnordMap;
1415
use rustc_errors::DiagInner;
1516
use rustc_index::IndexVec;
@@ -89,6 +90,38 @@ pub(crate) struct MarkFrame<'a> {
8990
parent: Option<&'a MarkFrame<'a>>,
9091
}
9192

93+
/// The edge list of one node being marked green: it occupies `buf[start..]` of the shared
94+
/// scratch buffer and is popped again on drop, restoring the buffer for the enclosing call.
95+
struct EdgeFrame<'a> {
96+
buf: &'a mut Vec<DepNodeIndex>,
97+
start: usize,
98+
}
99+
100+
impl<'a> EdgeFrame<'a> {
101+
#[inline]
102+
fn new(buf: &'a mut Vec<DepNodeIndex>) -> Self {
103+
EdgeFrame { start: buf.len(), buf }
104+
}
105+
106+
#[inline]
107+
fn push(&mut self, edge: DepNodeIndex) {
108+
self.buf.push(edge);
109+
}
110+
111+
/// The edges pushed onto this frame so far.
112+
#[inline]
113+
fn get(&self) -> &[DepNodeIndex] {
114+
&self.buf[self.start..]
115+
}
116+
}
117+
118+
impl Drop for EdgeFrame<'_> {
119+
#[inline]
120+
fn drop(&mut self) {
121+
self.buf.truncate(self.start);
122+
}
123+
}
124+
92125
#[derive(Debug)]
93126
pub(super) enum DepNodeColor {
94127
Green(DepNodeIndex),
@@ -119,6 +152,9 @@ pub struct DepGraphData {
119152
/// a particular query result was decoded from disk
120153
/// (not just marked green)
121154
debug_loaded_from_disk: Lock<FxHashSet<DepNode>>,
155+
156+
/// Per-worker edge buffer amortized across `try_mark_green` calls.
157+
green_edge_buf: WorkerLocal<Cell<Vec<DepNodeIndex>>>,
122158
}
123159

124160
pub fn hash_result<R>(hcx: &mut StableHashState<'_>, result: &R) -> Fingerprint
@@ -175,6 +211,7 @@ impl DepGraph {
175211
previous: prev_graph,
176212
colors,
177213
debug_loaded_from_disk: Default::default(),
214+
green_edge_buf: WorkerLocal::default(),
178215
})),
179216
virtual_dep_node_index: Arc::new(AtomicU32::new(0)),
180217
}
@@ -790,8 +827,9 @@ impl DepGraphData {
790827
fn promote_node_and_deps_to_current(
791828
&self,
792829
prev_index: SerializedDepNodeIndex,
830+
edges: &[DepNodeIndex],
793831
) -> Option<DepNodeIndex> {
794-
let dep_node_index = self.current.encoder.send_promoted(prev_index, &self.colors);
832+
let dep_node_index = self.current.encoder.send_promoted(prev_index, &self.colors, edges);
795833

796834
#[cfg(debug_assertions)]
797835
if let Some(dep_node_index) = dep_node_index {
@@ -878,20 +916,29 @@ impl DepGraphData {
878916
// in the previous compilation session too, so we can try to
879917
// mark it as green by recursively marking all of its
880918
// dependencies green.
881-
self.try_mark_previous_green(tcx, prev_index, None)
882-
.map(|dep_node_index| (prev_index, dep_node_index))
919+
920+
// Reuse a per-worker buffer for the edges instead of allocating one per call.
921+
// The recursion gives it back empty: each `EdgeFrame` pops its edges on drop.
922+
let mut edge_buf = self.green_edge_buf.take();
923+
let result = self.try_mark_previous_green(tcx, prev_index, None, &mut edge_buf);
924+
debug_assert!(edge_buf.is_empty());
925+
self.green_edge_buf.set(edge_buf);
926+
result.map(|dep_node_index| (prev_index, dep_node_index))
883927
}
884928
}
885929
}
886930

887931
/// Try to mark a dep-node which existed in the previous compilation session as green.
888-
#[instrument(skip(self, tcx, prev_dep_node_index, frame), level = "debug")]
932+
#[instrument(skip(self, tcx, prev_dep_node_index, frame, edge_buf), level = "debug")]
889933
fn try_mark_previous_green<'tcx>(
890934
&self,
891935
tcx: TyCtxt<'tcx>,
892936
prev_dep_node_index: SerializedDepNodeIndex,
893937
frame: Option<&MarkFrame<'_>>,
938+
// Amortized buffer to store edges in.
939+
edge_buf: &mut Vec<DepNodeIndex>,
894940
) -> Option<DepNodeIndex> {
941+
let mut edges = EdgeFrame::new(edge_buf);
895942
let frame = MarkFrame { index: prev_dep_node_index, parent: frame };
896943

897944
// We never try to mark eval_always nodes as green
@@ -901,7 +948,10 @@ impl DepGraphData {
901948
match self.colors.get(parent_dep_node_index) {
902949
// This dependency has been marked as green before, we are still ok and can
903950
// continue checking the remaining dependencies.
904-
DepNodeColor::Green(_) => continue,
951+
DepNodeColor::Green(parent_index) => {
952+
edges.push(parent_index);
953+
continue;
954+
}
905955

906956
// This dependency's result is different to the previous compilation session. We
907957
// cannot mark this dep_node as green, so stop checking.
@@ -915,8 +965,16 @@ impl DepGraphData {
915965

916966
// If this dependency isn't eval_always, try to mark it green recursively.
917967
if !tcx.is_eval_always(parent_dep_node.kind)
918-
&& self.try_mark_previous_green(tcx, parent_dep_node_index, Some(&frame)).is_some()
968+
&& let Some(parent_index) = self.try_mark_previous_green(
969+
tcx,
970+
parent_dep_node_index,
971+
Some(&frame),
972+
// Pass the edge buffer to the recursive call.
973+
// It will use an `EdgeFrame` to give it back unchanged.
974+
edges.buf,
975+
)
919976
{
977+
edges.push(parent_index);
920978
continue;
921979
}
922980

@@ -926,7 +984,10 @@ impl DepGraphData {
926984
}
927985

928986
match self.colors.get(parent_dep_node_index) {
929-
DepNodeColor::Green(_) => continue,
987+
DepNodeColor::Green(parent_index) => {
988+
edges.push(parent_index);
989+
continue;
990+
}
930991
DepNodeColor::Red => return None,
931992
DepNodeColor::Unknown => {}
932993
}
@@ -954,7 +1015,8 @@ impl DepGraphData {
9541015
// adding all the appropriate edges imported from the previous graph.
9551016
//
9561017
// `no_hash` nodes may fail this promotion due to already being conservatively colored red.
957-
let dep_node_index = self.promote_node_and_deps_to_current(prev_dep_node_index)?;
1018+
let dep_node_index =
1019+
self.promote_node_and_deps_to_current(prev_dep_node_index, edges.get())?;
9581020

9591021
// ... and finally storing a "Green" entry in the color map.
9601022
// Multiple threads can all write the same color here.

compiler/rustc_middle/src/dep_graph/serialized.rs

Lines changed: 15 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -603,16 +603,12 @@ impl NodeInfo {
603603
node: &DepNode,
604604
index: DepNodeIndex,
605605
value_fingerprint: Fingerprint,
606-
prev_index: SerializedDepNodeIndex,
607-
colors: &DepNodeColorMap,
608-
previous: &SerializedDepGraph,
606+
edges: &[DepNodeIndex],
609607
) -> usize {
610-
let edges = previous.edge_targets_from(prev_index);
611-
let edge_count = edges.size_hint().0;
608+
let edge_count = edges.len();
612609

613610
// Find the highest edge in the new dep node indices
614-
let edge_max =
615-
edges.clone().map(|i| colors.current(i).unwrap().as_u32()).max().unwrap_or(0);
611+
let edge_max = edges.iter().map(|x| x.as_u32()).max().unwrap_or(0);
616612

617613
let header =
618614
SerializedNodeHeader::new(node, index, value_fingerprint, edge_max, edge_count);
@@ -624,10 +620,10 @@ impl NodeInfo {
624620
}
625621

626622
let bytes_per_index = header.bytes_per_index();
627-
for node_index in edges {
628-
let node_index = colors.current(node_index).unwrap();
623+
for edge in edges {
624+
let edge = edge.as_u32();
629625
e.write_with(|dest| {
630-
*dest = node_index.as_u32().to_le_bytes();
626+
*dest = edge.to_le_bytes();
631627
bytes_per_index
632628
});
633629
}
@@ -728,25 +724,22 @@ impl EncoderState {
728724
node: &DepNode,
729725
index: DepNodeIndex,
730726
edge_count: usize,
731-
edges: impl FnOnce(&Self) -> Vec<DepNodeIndex>,
727+
edges: &[DepNodeIndex],
732728
retained_graph: &Option<Lock<RetainedDepGraph>>,
733729
local: &mut LocalEncoderState,
734730
) {
735731
local.kind_stats[node.kind.as_usize()] += 1;
736732
local.edge_count += edge_count;
737733

738734
if let Some(retained_graph) = &retained_graph {
739-
// Call `edges` before the outlined code to allow the closure to be optimized out.
740-
let edges = edges(self);
741-
742735
// Outline the build of the full dep graph as it's typically disabled and cold.
743736
outline(move || {
744737
// Block on the lock rather than using `try_lock`: under the parallel frontend
745738
// several threads record nodes concurrently, and dropping a node on lock
746739
// contention would make the retained graph nondeterministic. Readers take a
747740
// clone of the graph (`retained_dep_graph`) rather than holding the lock, so
748741
// this never deadlocks against a reentrant `record`.
749-
retained_graph.lock().push(index, *node, &edges);
742+
retained_graph.lock().push(index, *node, edges);
750743
});
751744
}
752745

@@ -783,14 +776,7 @@ impl EncoderState {
783776
) {
784777
node.encode(&mut local.encoder, index);
785778
self.flush_mem_encoder(&mut *local);
786-
self.record(
787-
&node.node,
788-
index,
789-
node.edges.len(),
790-
|_| node.edges[..].to_vec(),
791-
retained_graph,
792-
&mut *local,
793-
);
779+
self.record(&node.node, index, node.edges.len(), &node.edges, retained_graph, &mut *local);
794780
}
795781

796782
/// Encodes a node that was promoted from the previous graph. It reads the information directly from
@@ -805,34 +791,15 @@ impl EncoderState {
805791
index: DepNodeIndex,
806792
prev_index: SerializedDepNodeIndex,
807793
retained_graph: &Option<Lock<RetainedDepGraph>>,
808-
colors: &DepNodeColorMap,
809794
local: &mut LocalEncoderState,
795+
edges: &[DepNodeIndex],
810796
) {
811797
let node = self.previous.index_to_node(prev_index);
812798
let value_fingerprint = self.previous.value_fingerprint_for_index(prev_index);
813-
let edge_count = NodeInfo::encode_promoted(
814-
&mut local.encoder,
815-
node,
816-
index,
817-
value_fingerprint,
818-
prev_index,
819-
colors,
820-
&self.previous,
821-
);
799+
let edge_count =
800+
NodeInfo::encode_promoted(&mut local.encoder, node, index, value_fingerprint, edges);
822801
self.flush_mem_encoder(&mut *local);
823-
self.record(
824-
node,
825-
index,
826-
edge_count,
827-
|this| {
828-
this.previous
829-
.edge_targets_from(prev_index)
830-
.map(|i| colors.current(i).unwrap())
831-
.collect()
832-
},
833-
retained_graph,
834-
&mut *local,
835-
);
802+
self.record(node, index, edge_count, edges, retained_graph, &mut *local);
836803
}
837804

838805
fn finish(&self, profiler: &SelfProfilerRef, current: &CurrentDepGraph) -> FileEncodeResult {
@@ -1045,6 +1012,7 @@ impl GraphEncoder {
10451012
&self,
10461013
prev_index: SerializedDepNodeIndex,
10471014
colors: &DepNodeColorMap,
1015+
edges: &[DepNodeIndex],
10481016
) -> Option<DepNodeIndex> {
10491017
let _prof_timer = self.profiler.generic_activity("incr_comp_encode_dep_graph");
10501018

@@ -1060,8 +1028,8 @@ impl GraphEncoder {
10601028
index,
10611029
prev_index,
10621030
&self.retained_graph,
1063-
colors,
10641031
&mut *local,
1032+
edges,
10651033
);
10661034
Some(index)
10671035
}

0 commit comments

Comments
 (0)