@@ -315,19 +315,28 @@ impl SerializedDepGraph {
315315
316316 // `node_max` is the number of indices including empty nodes while `node_count`
317317 // is the number of actually encoded nodes.
318- let ( node_max, node_count, edge_count) =
319- d. with_position ( d. len ( ) - 3 * IntEncodedWithFixedSize :: ENCODED_SIZE , |d| {
318+ //
319+ // The trailer holds the fixed-size node/edge counts followed by the per-kind
320+ // node counts, so all of them can be read by seeking from the end of the file.
321+ // Reading the per-kind counts up front lets us bucket nodes by kind during the
322+ // single decode pass below, rather than in a separate counting-sort pass.
323+ let kind_count = DepKind :: MAX as usize + 1 ;
324+ let trailer_len = ( 3 + kind_count) * IntEncodedWithFixedSize :: ENCODED_SIZE ;
325+ let ( node_max, node_count, edge_count, kind_counts) =
326+ d. with_position ( d. len ( ) - trailer_len, |d| {
320327 debug ! ( "position: {:?}" , d. position( ) ) ;
321328 let node_max = IntEncodedWithFixedSize :: decode ( d) . 0 as usize ;
322329 let node_count = IntEncodedWithFixedSize :: decode ( d) . 0 as usize ;
323330 let edge_count = IntEncodedWithFixedSize :: decode ( d) . 0 as usize ;
324- ( node_max, node_count, edge_count)
331+ let kind_counts: Vec < u32 > =
332+ ( 0 ..kind_count) . map ( |_| IntEncodedWithFixedSize :: decode ( d) . 0 as u32 ) . collect ( ) ;
333+ ( node_max, node_count, edge_count, kind_counts)
325334 } ) ;
326335 debug ! ( "position: {:?}" , d. position( ) ) ;
327336
328337 debug ! ( ?node_count, ?edge_count) ;
329338
330- let graph_bytes = d. len ( ) - ( 3 * IntEncodedWithFixedSize :: ENCODED_SIZE ) - d. position ( ) ;
339+ let graph_bytes = d. len ( ) - trailer_len - d. position ( ) ;
331340
332341 let mut nodes = IndexVec :: from_elem_n (
333342 DepNode {
@@ -352,6 +361,19 @@ impl SerializedDepGraph {
352361 let mut edge_list_data =
353362 Vec :: with_capacity ( graph_bytes - node_count * size_of :: < SerializedNodeHeader > ( ) ) ;
354363
364+ // Lay out the contiguous per-`DepKind` ranges of `nodes_by_kind` from the
365+ // counts read above, so each node can be placed into its kind's range as it
366+ // is decoded below. `fill[k]` is the next free slot in kind `k`'s range.
367+ let mut kinds = Vec :: with_capacity ( kind_count) ;
368+ let mut offset = 0u32 ;
369+ for & len in & kind_counts {
370+ kinds. push ( LazyKindIndex { start : offset, len, map : OnceLock :: new ( ) } ) ;
371+ offset += len;
372+ }
373+ debug_assert_eq ! ( offset as usize , node_count) ;
374+ let mut nodes_by_kind = vec ! [ None ; node_count] ;
375+ let mut fill: Vec < u32 > = kinds. iter ( ) . map ( |k| k. start ) . collect ( ) ;
376+
355377 for _ in 0 ..node_count {
356378 // Decode the header for this edge; the header packs together as many of the fixed-size
357379 // fields as possible to limit the number of times we update decoder state.
@@ -364,6 +386,12 @@ impl SerializedDepGraph {
364386 assert ! ( node_header. node( ) . kind != DepKind :: Null && node. kind == DepKind :: Null ) ;
365387 * node = node_header. node ( ) ;
366388
389+ // Place this node into its kind's range for the lazy reverse index. Only
390+ // encoded (non-`Null`) nodes reach this loop, so each fills one slot once.
391+ let k = node. kind . as_usize ( ) ;
392+ nodes_by_kind[ fill[ k] as usize ] = Some ( index) ;
393+ fill[ k] += 1 ;
394+
367395 value_fingerprints[ index] = node_header. value_fingerprint ( ) ;
368396
369397 // If the length of this node's edge list is small, the length is stored in the header.
@@ -388,36 +416,9 @@ impl SerializedDepGraph {
388416 // end of the array. This padding ensure it doesn't.
389417 edge_list_data. extend ( & [ 0u8 ; DEP_NODE_PAD ] ) ;
390418
391- // Read the number of nodes of each dep kind, and perform
392- // counting sort for `LazyNodeIndex`.
393- let mut kinds = Vec :: with_capacity ( DepKind :: MAX as usize + 1 ) ;
394- let mut offset = 0u32 ;
395- for _ in 0 ..( DepKind :: MAX + 1 ) {
396- let len = d. read_u32 ( ) ;
397- kinds. push ( LazyKindIndex { start : offset, len, map : OnceLock :: new ( ) } ) ;
398- offset += len;
399- }
400- debug_assert_eq ! ( offset as usize , node_count) ;
401-
402419 let session_count = d. read_u64 ( ) ;
403420
404- // Counting sort: place each node index into its kind's range. `fill[k]`
405- // points at the next free slot in kind `k`'s range, so a kind's nodes end
406- // up contiguous. Slots start as `None` and are each filled exactly once
407- // (the counts sum to the number of non-`Null` nodes).
408- let mut nodes_by_kind = vec ! [ None ; node_count] ;
409- let mut fill: Vec < u32 > = kinds. iter ( ) . map ( |k| k. start ) . collect ( ) ;
410- for ( idx, node) in nodes. iter_enumerated ( ) {
411- // Unused indices from batch allocation stay `Null`; they carry no
412- // encoded node and are never looked up by fingerprint, so skip them.
413- if node. kind == DepKind :: Null {
414- continue ;
415- }
416- let k = node. kind . as_usize ( ) ;
417- nodes_by_kind[ fill[ k] as usize ] = Some ( idx) ;
418- fill[ k] += 1 ;
419- }
420- // Each kind's range was filled exactly to its end.
421+ // Each kind's range was filled exactly to its end by the decode loop.
421422 debug_assert ! ( kinds. iter( ) . zip( & fill) . all( |( k, & f) | f == k. start + k. len) ) ;
422423 let reverse_index = LazyNodeIndex { nodes_by_kind, kinds } ;
423424
@@ -885,20 +886,22 @@ impl EncoderState {
885886 }
886887 }
887888
888- // Encode the number of each dep kind encountered
889- for count in kind_stats. iter ( ) {
890- count. encode ( & mut encoder) ;
891- }
892-
889+ // The session count is read sequentially right after the node data.
893890 self . previous . session_count . checked_add ( 1 ) . unwrap ( ) . encode ( & mut encoder) ;
894891
895892 debug ! ( ?node_max, ?node_count, ?edge_count) ;
896893 debug ! ( "position: {:?}" , encoder. position( ) ) ;
894+ // Fixed-size trailer: the node/edge counts followed by the number of nodes of
895+ // each dep kind, all readable by seeking from the end of the file. The decoder
896+ // reads the per-kind counts up front to bucket nodes by kind in a single pass.
897897 IntEncodedWithFixedSize ( node_max. try_into ( ) . unwrap ( ) ) . encode ( & mut encoder) ;
898898 IntEncodedWithFixedSize ( node_count. try_into ( ) . unwrap ( ) ) . encode ( & mut encoder) ;
899899 IntEncodedWithFixedSize ( edge_count. try_into ( ) . unwrap ( ) ) . encode ( & mut encoder) ;
900+ for count in kind_stats. iter ( ) {
901+ IntEncodedWithFixedSize ( * count as u64 ) . encode ( & mut encoder) ;
902+ }
900903 debug ! ( "position: {:?}" , encoder. position( ) ) ;
901- // Drop the encoder so that nothing is written after the counts .
904+ // Drop the encoder so that nothing is written after the trailer .
902905 let result = encoder. finish ( ) ;
903906 if let Ok ( position) = result {
904907 // FIXME(rylev): we hardcode the dep graph file name so we
0 commit comments