Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 4563f70

Browse files
committedApr 5, 2024
Auto merge of #122070 - Zoxc:dep-edges-from-previous, r=cjgillot
Encode dep graph edges directly from the previous graph when promoting This encodes dep graph edges directly from the previous graph when promoting nodes from a previous session, avoiding allocations / copies. ~~Based on #122064 and #116375 <table><tr><td rowspan="2">Benchmark</td><td colspan="1"><b>Before</b></th><td colspan="2"><b>After</b></th></tr><tr><td align="right">Time</td><td align="right">Time</td><td align="right">%</th></tr><tr><td>🟣 <b>clap</b>:check:unchanged</td><td align="right">0.4177s</td><td align="right">0.4072s</td><td align="right">💚 -2.52%</td></tr><tr><td>🟣 <b>hyper</b>:check:unchanged</td><td align="right">0.1430s</td><td align="right">0.1420s</td><td align="right"> -0.69%</td></tr><tr><td>🟣 <b>regex</b>:check:unchanged</td><td align="right">0.3106s</td><td align="right">0.3038s</td><td align="right">💚 -2.19%</td></tr><tr><td>🟣 <b>syn</b>:check:unchanged</td><td align="right">0.5823s</td><td align="right">0.5688s</td><td align="right">💚 -2.33%</td></tr><tr><td>🟣 <b>syntex_syntax</b>:check:unchanged</td><td align="right">1.3992s</td><td align="right">1.3692s</td><td align="right">💚 -2.14%</td></tr><tr><td>Total</td><td align="right">2.8528s</td><td align="right">2.7910s</td><td align="right">💚 -2.17%</td></tr><tr><td>Summary</td><td align="right">1.0000s</td><td align="right">0.9803s</td><td align="right">💚 -1.97%</td></tr></table>
2 parents d009f60 + aa9c9a3 commit 4563f70

File tree

4 files changed

+183
-45
lines changed

4 files changed

+183
-45
lines changed
 

‎compiler/rustc_incremental/src/persist/load.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use rustc_session::config::IncrementalStateAssertion;
1111
use rustc_session::Session;
1212
use rustc_span::ErrorGuaranteed;
1313
use std::path::{Path, PathBuf};
14+
use std::sync::Arc;
1415

1516
use super::data::*;
1617
use super::file_format;
@@ -88,7 +89,7 @@ fn delete_dirty_work_product(sess: &Session, swp: SerializedWorkProduct) {
8889
work_product::delete_workproduct_files(sess, &swp.work_product);
8990
}
9091

91-
fn load_dep_graph(sess: &Session) -> LoadResult<(SerializedDepGraph, WorkProductMap)> {
92+
fn load_dep_graph(sess: &Session) -> LoadResult<(Arc<SerializedDepGraph>, WorkProductMap)> {
9293
let prof = sess.prof.clone();
9394

9495
if sess.opts.incremental.is_none() {

‎compiler/rustc_incremental/src/persist/save.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use rustc_serialize::opaque::{FileEncodeResult, FileEncoder};
1010
use rustc_serialize::Encodable as RustcEncodable;
1111
use rustc_session::Session;
1212
use std::fs;
13+
use std::sync::Arc;
1314

1415
use super::data::*;
1516
use super::dirty_clean;
@@ -147,7 +148,7 @@ fn encode_query_cache(tcx: TyCtxt<'_>, encoder: FileEncoder) -> FileEncodeResult
147148
/// and moves it to the permanent dep-graph path
148149
pub(crate) fn build_dep_graph(
149150
sess: &Session,
150-
prev_graph: SerializedDepGraph,
151+
prev_graph: Arc<SerializedDepGraph>,
151152
prev_work_products: WorkProductMap,
152153
) -> Option<DepGraph> {
153154
if sess.opts.incremental.is_none() {

‎compiler/rustc_query_system/src/dep_graph/graph.rs

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ use std::fmt::Debug;
1313
use std::hash::Hash;
1414
use std::marker::PhantomData;
1515
use std::sync::atomic::Ordering;
16+
use std::sync::Arc;
1617

1718
use super::query::DepGraphQuery;
1819
use super::serialized::{GraphEncoder, SerializedDepGraph, SerializedDepNodeIndex};
@@ -81,7 +82,7 @@ pub(crate) struct DepGraphData<D: Deps> {
8182

8283
/// The dep-graph from the previous compilation session. It contains all
8384
/// nodes and edges as well as all fingerprints of nodes that have them.
84-
previous: SerializedDepGraph,
85+
previous: Arc<SerializedDepGraph>,
8586

8687
colors: DepNodeColorMap,
8788

@@ -113,7 +114,7 @@ where
113114
impl<D: Deps> DepGraph<D> {
114115
pub fn new(
115116
profiler: &SelfProfilerRef,
116-
prev_graph: SerializedDepGraph,
117+
prev_graph: Arc<SerializedDepGraph>,
117118
prev_work_products: WorkProductMap,
118119
encoder: FileEncoder,
119120
record_graph: bool,
@@ -127,6 +128,7 @@ impl<D: Deps> DepGraph<D> {
127128
encoder,
128129
record_graph,
129130
record_stats,
131+
prev_graph.clone(),
130132
);
131133

132134
let colors = DepNodeColorMap::new(prev_graph_node_count);
@@ -1084,6 +1086,7 @@ impl<D: Deps> CurrentDepGraph<D> {
10841086
encoder: FileEncoder,
10851087
record_graph: bool,
10861088
record_stats: bool,
1089+
previous: Arc<SerializedDepGraph>,
10871090
) -> Self {
10881091
use std::time::{SystemTime, UNIX_EPOCH};
10891092

@@ -1116,6 +1119,7 @@ impl<D: Deps> CurrentDepGraph<D> {
11161119
record_graph,
11171120
record_stats,
11181121
profiler,
1122+
previous,
11191123
),
11201124
new_node_to_index: Sharded::new(|| {
11211125
FxHashMap::with_capacity_and_hasher(
@@ -1236,16 +1240,14 @@ impl<D: Deps> CurrentDepGraph<D> {
12361240
match prev_index_to_index[prev_index] {
12371241
Some(dep_node_index) => dep_node_index,
12381242
None => {
1239-
let key = prev_graph.index_to_node(prev_index);
1240-
let edges = prev_graph
1241-
.edge_targets_from(prev_index)
1242-
.map(|i| prev_index_to_index[i].unwrap())
1243-
.collect();
1244-
let fingerprint = prev_graph.fingerprint_by_index(prev_index);
1245-
let dep_node_index = self.encoder.send(key, fingerprint, edges);
1243+
let dep_node_index = self.encoder.send_promoted(prev_index, &*prev_index_to_index);
12461244
prev_index_to_index[prev_index] = Some(dep_node_index);
12471245
#[cfg(debug_assertions)]
1248-
self.record_edge(dep_node_index, key, fingerprint);
1246+
self.record_edge(
1247+
dep_node_index,
1248+
prev_graph.index_to_node(prev_index),
1249+
prev_graph.fingerprint_by_index(prev_index),
1250+
);
12491251
dep_node_index
12501252
}
12511253
}

‎compiler/rustc_query_system/src/dep_graph/serialized.rs

Lines changed: 167 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ use crate::dep_graph::edges::EdgesVec;
4141
use rustc_data_structures::fingerprint::Fingerprint;
4242
use rustc_data_structures::fingerprint::PackedFingerprint;
4343
use rustc_data_structures::fx::FxHashMap;
44+
use rustc_data_structures::outline;
4445
use rustc_data_structures::profiling::SelfProfilerRef;
4546
use rustc_data_structures::sync::Lock;
4647
use rustc_data_structures::unhash::UnhashMap;
@@ -49,6 +50,7 @@ use rustc_serialize::opaque::{FileEncodeResult, FileEncoder, IntEncodedWithFixed
4950
use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};
5051
use std::iter;
5152
use std::marker::PhantomData;
53+
use std::sync::Arc;
5254

5355
// The maximum value of `SerializedDepNodeIndex` leaves the upper two bits
5456
// unused so that we can store multiple index types in `CompressedHybridIndex`,
@@ -94,7 +96,7 @@ impl SerializedDepGraph {
9496
pub fn edge_targets_from(
9597
&self,
9698
source: SerializedDepNodeIndex,
97-
) -> impl Iterator<Item = SerializedDepNodeIndex> + '_ {
99+
) -> impl Iterator<Item = SerializedDepNodeIndex> + Clone + '_ {
98100
let header = self.edge_list_indices[source];
99101
let mut raw = &self.edge_list_data[header.start()..];
100102
// Figure out where the edge list for `source` ends by getting the start index of the next
@@ -176,7 +178,7 @@ fn mask(bits: usize) -> usize {
176178

177179
impl SerializedDepGraph {
178180
#[instrument(level = "debug", skip(d))]
179-
pub fn decode<D: Deps>(d: &mut MemDecoder<'_>) -> SerializedDepGraph {
181+
pub fn decode<D: Deps>(d: &mut MemDecoder<'_>) -> Arc<SerializedDepGraph> {
180182
// The last 16 bytes are the node count and edge count.
181183
debug!("position: {:?}", d.position());
182184
let (node_count, edge_count, graph_size) =
@@ -254,7 +256,13 @@ impl SerializedDepGraph {
254256
index[node.kind.as_usize()].insert(node.hash, idx);
255257
}
256258

257-
SerializedDepGraph { nodes, fingerprints, edge_list_indices, edge_list_data, index }
259+
Arc::new(SerializedDepGraph {
260+
nodes,
261+
fingerprints,
262+
edge_list_indices,
263+
edge_list_data,
264+
index,
265+
})
258266
}
259267
}
260268

@@ -299,21 +307,24 @@ impl<D: Deps> SerializedNodeHeader<D> {
299307
const MAX_INLINE_LEN: usize = (u16::MAX as usize >> (Self::TOTAL_BITS - Self::LEN_BITS)) - 1;
300308

301309
#[inline]
302-
fn new(node_info: &NodeInfo) -> Self {
310+
fn new(
311+
node: DepNode,
312+
fingerprint: Fingerprint,
313+
edge_max_index: u32,
314+
edge_count: usize,
315+
) -> Self {
303316
debug_assert_eq!(Self::TOTAL_BITS, Self::LEN_BITS + Self::WIDTH_BITS + Self::KIND_BITS);
304317

305-
let NodeInfo { node, fingerprint, edges } = node_info;
306-
307318
let mut head = node.kind.as_inner();
308319

309-
let free_bytes = edges.max_index().leading_zeros() as usize / 8;
320+
let free_bytes = edge_max_index.leading_zeros() as usize / 8;
310321
let bytes_per_index = (DEP_NODE_SIZE - free_bytes).saturating_sub(1);
311322
head |= (bytes_per_index as u16) << Self::KIND_BITS;
312323

313324
// Encode number of edges + 1 so that we can reserve 0 to indicate that the len doesn't fit
314325
// in this bitfield.
315-
if edges.len() <= Self::MAX_INLINE_LEN {
316-
head |= (edges.len() as u16 + 1) << (Self::KIND_BITS + Self::WIDTH_BITS);
326+
if edge_count <= Self::MAX_INLINE_LEN {
327+
head |= (edge_count as u16 + 1) << (Self::KIND_BITS + Self::WIDTH_BITS);
317328
}
318329

319330
let hash: Fingerprint = node.hash.into();
@@ -327,10 +338,10 @@ impl<D: Deps> SerializedNodeHeader<D> {
327338
#[cfg(debug_assertions)]
328339
{
329340
let res = Self { bytes, _marker: PhantomData };
330-
assert_eq!(node_info.fingerprint, res.fingerprint());
331-
assert_eq!(node_info.node, res.node());
341+
assert_eq!(fingerprint, res.fingerprint());
342+
assert_eq!(node, res.node());
332343
if let Some(len) = res.len() {
333-
assert_eq!(node_info.edges.len(), len);
344+
assert_eq!(edge_count, len);
334345
}
335346
}
336347
Self { bytes, _marker: PhantomData }
@@ -393,21 +404,61 @@ struct NodeInfo {
393404

394405
impl NodeInfo {
395406
fn encode<D: Deps>(&self, e: &mut FileEncoder) {
396-
let header = SerializedNodeHeader::<D>::new(self);
407+
let NodeInfo { node, fingerprint, ref edges } = *self;
408+
let header =
409+
SerializedNodeHeader::<D>::new(node, fingerprint, edges.max_index(), edges.len());
397410
e.write_array(header.bytes);
398411

399412
if header.len().is_none() {
400-
e.emit_usize(self.edges.len());
413+
e.emit_usize(edges.len());
401414
}
402415

403416
let bytes_per_index = header.bytes_per_index();
404-
for node_index in self.edges.iter() {
417+
for node_index in edges.iter() {
405418
e.write_with(|dest| {
406419
*dest = node_index.as_u32().to_le_bytes();
407420
bytes_per_index
408421
});
409422
}
410423
}
424+
425+
/// Encode a node that was promoted from the previous graph. It reads the edges directly from
426+
/// the previous dep graph and expects all edges to already have a new dep node index assigned.
427+
/// This avoids the overhead of constructing `EdgesVec`, which would be needed to call `encode`.
428+
#[inline]
429+
fn encode_promoted<D: Deps>(
430+
e: &mut FileEncoder,
431+
node: DepNode,
432+
fingerprint: Fingerprint,
433+
prev_index: SerializedDepNodeIndex,
434+
prev_index_to_index: &IndexVec<SerializedDepNodeIndex, Option<DepNodeIndex>>,
435+
previous: &SerializedDepGraph,
436+
) -> usize {
437+
let edges = previous.edge_targets_from(prev_index);
438+
let edge_count = edges.size_hint().0;
439+
440+
// Find the highest edge in the new dep node indices
441+
let edge_max =
442+
edges.clone().map(|i| prev_index_to_index[i].unwrap().as_u32()).max().unwrap_or(0);
443+
444+
let header = SerializedNodeHeader::<D>::new(node, fingerprint, edge_max, edge_count);
445+
e.write_array(header.bytes);
446+
447+
if header.len().is_none() {
448+
e.emit_usize(edge_count);
449+
}
450+
451+
let bytes_per_index = header.bytes_per_index();
452+
for node_index in edges {
453+
let node_index = prev_index_to_index[node_index].unwrap();
454+
e.write_with(|dest| {
455+
*dest = node_index.as_u32().to_le_bytes();
456+
bytes_per_index
457+
});
458+
}
459+
460+
edge_count
461+
}
411462
}
412463

413464
struct Stat {
@@ -417,6 +468,7 @@ struct Stat {
417468
}
418469

419470
struct EncoderState<D: Deps> {
471+
previous: Arc<SerializedDepGraph>,
420472
encoder: FileEncoder,
421473
total_node_count: usize,
422474
total_edge_count: usize,
@@ -428,8 +480,9 @@ struct EncoderState<D: Deps> {
428480
}
429481

430482
impl<D: Deps> EncoderState<D> {
431-
fn new(encoder: FileEncoder, record_stats: bool) -> Self {
483+
fn new(encoder: FileEncoder, record_stats: bool, previous: Arc<SerializedDepGraph>) -> Self {
432484
Self {
485+
previous,
433486
encoder,
434487
total_edge_count: 0,
435488
total_node_count: 0,
@@ -439,38 +492,101 @@ impl<D: Deps> EncoderState<D> {
439492
}
440493
}
441494

442-
fn encode_node(
495+
#[inline]
496+
fn record(
443497
&mut self,
444-
node: &NodeInfo,
498+
node: DepNode,
499+
edge_count: usize,
500+
edges: impl FnOnce(&mut Self) -> Vec<DepNodeIndex>,
445501
record_graph: &Option<Lock<DepGraphQuery>>,
446502
) -> DepNodeIndex {
447503
let index = DepNodeIndex::new(self.total_node_count);
448-
self.total_node_count += 1;
449-
self.kind_stats[node.node.kind.as_usize()] += 1;
450504

451-
let edge_count = node.edges.len();
505+
self.total_node_count += 1;
506+
self.kind_stats[node.kind.as_usize()] += 1;
452507
self.total_edge_count += edge_count;
453508

454509
if let Some(record_graph) = &record_graph {
455-
// Do not ICE when a query is called from within `with_query`.
456-
if let Some(record_graph) = &mut record_graph.try_lock() {
457-
record_graph.push(index, node.node, &node.edges);
458-
}
510+
// Call `edges` before the outlined code to allow the closure to be optimized out.
511+
let edges = edges(self);
512+
513+
// Outline the build of the full dep graph as it's typically disabled and cold.
514+
outline(move || {
515+
// Do not ICE when a query is called from within `with_query`.
516+
if let Some(record_graph) = &mut record_graph.try_lock() {
517+
record_graph.push(index, node, &edges);
518+
}
519+
});
459520
}
460521

461522
if let Some(stats) = &mut self.stats {
462-
let kind = node.node.kind;
463-
464-
let stat = stats.entry(kind).or_insert(Stat { kind, node_counter: 0, edge_counter: 0 });
465-
stat.node_counter += 1;
466-
stat.edge_counter += edge_count as u64;
523+
let kind = node.kind;
524+
525+
// Outline the stats code as it's typically disabled and cold.
526+
outline(move || {
527+
let stat =
528+
stats.entry(kind).or_insert(Stat { kind, node_counter: 0, edge_counter: 0 });
529+
stat.node_counter += 1;
530+
stat.edge_counter += edge_count as u64;
531+
});
467532
}
468533

469-
let encoder = &mut self.encoder;
470-
node.encode::<D>(encoder);
471534
index
472535
}
473536

537+
/// Encodes a node to the current graph.
538+
fn encode_node(
539+
&mut self,
540+
node: &NodeInfo,
541+
record_graph: &Option<Lock<DepGraphQuery>>,
542+
) -> DepNodeIndex {
543+
node.encode::<D>(&mut self.encoder);
544+
self.record(
545+
node.node,
546+
node.edges.len(),
547+
|_| node.edges[..].iter().copied().collect(),
548+
record_graph,
549+
)
550+
}
551+
552+
/// Encodes a node that was promoted from the previous graph. It reads the information directly from
553+
/// the previous dep graph for performance reasons.
554+
///
555+
/// This differs from `encode_node` where you have to explictly provide the relevant `NodeInfo`.
556+
///
557+
/// It expects all edges to already have a new dep node index assigned.
558+
#[inline]
559+
fn encode_promoted_node(
560+
&mut self,
561+
prev_index: SerializedDepNodeIndex,
562+
record_graph: &Option<Lock<DepGraphQuery>>,
563+
prev_index_to_index: &IndexVec<SerializedDepNodeIndex, Option<DepNodeIndex>>,
564+
) -> DepNodeIndex {
565+
let node = self.previous.index_to_node(prev_index);
566+
567+
let fingerprint = self.previous.fingerprint_by_index(prev_index);
568+
let edge_count = NodeInfo::encode_promoted::<D>(
569+
&mut self.encoder,
570+
node,
571+
fingerprint,
572+
prev_index,
573+
prev_index_to_index,
574+
&self.previous,
575+
);
576+
577+
self.record(
578+
node,
579+
edge_count,
580+
|this| {
581+
this.previous
582+
.edge_targets_from(prev_index)
583+
.map(|i| prev_index_to_index[i].unwrap())
584+
.collect()
585+
},
586+
record_graph,
587+
)
588+
}
589+
474590
fn finish(self, profiler: &SelfProfilerRef) -> FileEncodeResult {
475591
let Self {
476592
mut encoder,
@@ -479,6 +595,7 @@ impl<D: Deps> EncoderState<D> {
479595
stats: _,
480596
kind_stats,
481597
marker: _,
598+
previous: _,
482599
} = self;
483600

484601
let node_count = total_node_count.try_into().unwrap();
@@ -520,9 +637,10 @@ impl<D: Deps> GraphEncoder<D> {
520637
record_graph: bool,
521638
record_stats: bool,
522639
profiler: &SelfProfilerRef,
640+
previous: Arc<SerializedDepGraph>,
523641
) -> Self {
524642
let record_graph = record_graph.then(|| Lock::new(DepGraphQuery::new(prev_node_count)));
525-
let status = Lock::new(Some(EncoderState::new(encoder, record_stats)));
643+
let status = Lock::new(Some(EncoderState::new(encoder, record_stats, previous)));
526644
GraphEncoder { status, record_graph, profiler: profiler.clone() }
527645
}
528646

@@ -596,6 +714,22 @@ impl<D: Deps> GraphEncoder<D> {
596714
self.status.lock().as_mut().unwrap().encode_node(&node, &self.record_graph)
597715
}
598716

717+
/// Encodes a node that was promoted from the previous graph. It reads the information directly from
718+
/// the previous dep graph and expects all edges to already have a new dep node index assigned.
719+
#[inline]
720+
pub(crate) fn send_promoted(
721+
&self,
722+
prev_index: SerializedDepNodeIndex,
723+
prev_index_to_index: &IndexVec<SerializedDepNodeIndex, Option<DepNodeIndex>>,
724+
) -> DepNodeIndex {
725+
let _prof_timer = self.profiler.generic_activity("incr_comp_encode_dep_graph");
726+
self.status.lock().as_mut().unwrap().encode_promoted_node(
727+
prev_index,
728+
&self.record_graph,
729+
prev_index_to_index,
730+
)
731+
}
732+
599733
pub fn finish(&self) -> FileEncodeResult {
600734
let _prof_timer = self.profiler.generic_activity("incr_comp_encode_dep_graph_finish");
601735

0 commit comments

Comments
 (0)
Please sign in to comment.