@@ -13,6 +13,7 @@ use crate::util::{CargoResult, GlobalContext};
13
13
use anyhow:: Context as _;
14
14
use cargo_util:: paths;
15
15
use indexmap:: IndexMap ;
16
+ use itertools:: Itertools ;
16
17
use std:: collections:: HashMap ;
17
18
use std:: io:: { BufWriter , Write } ;
18
19
use std:: thread:: available_parallelism;
@@ -97,6 +98,29 @@ struct UnitTime {
97
98
sections : IndexMap < String , CompilationSection > ,
98
99
}
99
100
101
+ const FRONTEND_SECTION_NAME : & str = "Frontend" ;
102
+ const CODEGEN_SECTION_NAME : & str = "Codegen" ;
103
+
104
+ impl UnitTime {
105
+ fn aggregate_sections ( & self ) -> AggregatedSections {
106
+ let end = self . duration ;
107
+
108
+ if let Some ( rmeta) = self . rmeta_time {
109
+ // We only know when the rmeta time was generated
110
+ AggregatedSections :: OnlyMetadataTime {
111
+ frontend : SectionData {
112
+ start : 0.0 ,
113
+ end : rmeta,
114
+ } ,
115
+ codegen : SectionData { start : rmeta, end } ,
116
+ }
117
+ } else {
118
+ // We only know the total duration
119
+ AggregatedSections :: OnlyTotalDuration
120
+ }
121
+ }
122
+ }
123
+
100
124
/// Periodic concurrency tracking information.
101
125
#[ derive( serde:: Serialize ) ]
102
126
struct Concurrency {
@@ -111,6 +135,33 @@ struct Concurrency {
111
135
inactive : usize ,
112
136
}
113
137
138
+ /// Postprocessed section data that has both start and an end.
139
+ #[ derive( Copy , Clone , serde:: Serialize ) ]
140
+ struct SectionData {
141
+ /// Start (relative to the start of the unit)
142
+ start : f64 ,
143
+ /// End (relative to the start of the unit)
144
+ end : f64 ,
145
+ }
146
+
147
+ impl SectionData {
148
+ fn duration ( & self ) -> f64 {
149
+ ( self . end - self . start ) . max ( 0.0 )
150
+ }
151
+ }
152
+
153
+ /// Contains post-processed data of individual compilation sections.
154
+ #[ derive( serde:: Serialize ) ]
155
+ enum AggregatedSections {
156
+ /// We only know when .rmeta was generated, so we can distill frontend and codegen time.
157
+ OnlyMetadataTime {
158
+ frontend : SectionData ,
159
+ codegen : SectionData ,
160
+ } ,
161
+ /// We know only the total duration
162
+ OnlyTotalDuration ,
163
+ }
164
+
114
165
impl < ' gctx > Timings < ' gctx > {
115
166
pub fn new ( bcx : & BuildContext < ' _ , ' gctx > , root_units : & [ Unit ] ) -> Timings < ' gctx > {
116
167
let has_report = |what| bcx. build_config . timing_outputs . contains ( & what) ;
@@ -555,6 +606,29 @@ impl<'gctx> Timings<'gctx> {
555
606
556
607
/// Render the table of all units.
557
608
fn write_unit_table ( & self , f : & mut impl Write ) -> CargoResult < ( ) > {
609
+ let mut units: Vec < & UnitTime > = self . unit_times . iter ( ) . collect ( ) ;
610
+ units. sort_unstable_by ( |a, b| b. duration . partial_cmp ( & a. duration ) . unwrap ( ) ) ;
611
+
612
+ // We can have a bunch of situations here.
613
+ // - -Zsection-timings is enabled, and we received some custom sections, in which
614
+ // case we use them to determine the headers.
615
+ // - We have at least one rmeta time, so we hard-code Frontend and Codegen headers.
616
+ // - We only have total durations, so we don't add any additional headers.
617
+ let aggregated: Vec < AggregatedSections > =
618
+ units. iter ( ) . map ( |u| u. aggregate_sections ( ) ) . collect ( ) ;
619
+
620
+ let headers: Vec < String > = if aggregated
621
+ . iter ( )
622
+ . any ( |s| matches ! ( s, AggregatedSections :: OnlyMetadataTime { .. } ) )
623
+ {
624
+ vec ! [
625
+ FRONTEND_SECTION_NAME . to_string( ) ,
626
+ CODEGEN_SECTION_NAME . to_string( ) ,
627
+ ]
628
+ } else {
629
+ vec ! [ ]
630
+ } ;
631
+
558
632
write ! (
559
633
f,
560
634
r#"
@@ -564,20 +638,48 @@ impl<'gctx> Timings<'gctx> {
564
638
<th></th>
565
639
<th>Unit</th>
566
640
<th>Total</th>
567
- <th>Codegen</th>
641
+ {headers}
568
642
<th>Features</th>
569
643
</tr>
570
644
</thead>
571
645
<tbody>
572
- "#
646
+ "# ,
647
+ headers = headers. iter( ) . map( |h| format!( "<th>{h}</th>" ) ) . join( "\n " )
573
648
) ?;
574
- let mut units: Vec < & UnitTime > = self . unit_times . iter ( ) . collect ( ) ;
575
- units. sort_unstable_by ( |a, b| b. duration . partial_cmp ( & a. duration ) . unwrap ( ) ) ;
576
- for ( i, unit) in units. iter ( ) . enumerate ( ) {
577
- let codegen = match unit. codegen_time ( ) {
649
+
650
+ for ( i, ( unit, aggregated_sections) ) in units. iter ( ) . zip ( aggregated) . enumerate ( ) {
651
+ let format_duration = |section : Option < SectionData > | match section {
652
+ Some ( section) => {
653
+ let duration = section. duration ( ) ;
654
+ let pct = ( duration / unit. duration ) * 100.0 ;
655
+ format ! ( "{duration:.1}s ({:.0}%)" , pct)
656
+ }
578
657
None => "" . to_string ( ) ,
579
- Some ( ( _rt, ctime, cent) ) => format ! ( "{:.1}s ({:.0}%)" , ctime, cent) ,
580
658
} ;
659
+
660
+ // This is a bit complex, as we assume the most general option - we can have an
661
+ // arbitrary set of headers, and an arbitrary set of sections per unit, so we always
662
+ // initiate the cells to be empty, and then try to find a corresponding column for which
663
+ // we might have data.
664
+ let mut cells: HashMap < & str , SectionData > = Default :: default ( ) ;
665
+
666
+ match & aggregated_sections {
667
+ AggregatedSections :: OnlyMetadataTime { frontend, codegen } => {
668
+ cells. insert ( FRONTEND_SECTION_NAME , * frontend) ;
669
+ cells. insert ( CODEGEN_SECTION_NAME , * codegen) ;
670
+ }
671
+ AggregatedSections :: OnlyTotalDuration => { }
672
+ } ;
673
+ let cells = headers
674
+ . iter ( )
675
+ . map ( |header| {
676
+ format ! (
677
+ "<td>{}</td>" ,
678
+ format_duration( cells. remove( header. as_str( ) ) )
679
+ )
680
+ } )
681
+ . join ( "\n " ) ;
682
+
581
683
let features = unit. unit . features . join ( ", " ) ;
582
684
write ! (
583
685
f,
@@ -586,16 +688,14 @@ impl<'gctx> Timings<'gctx> {
586
688
<td>{}.</td>
587
689
<td>{}{}</td>
588
690
<td>{:.1}s</td>
589
- <td>{}</td>
590
- <td>{}</td>
691
+ {cells}
692
+ <td>{features }</td>
591
693
</tr>
592
694
"# ,
593
695
i + 1 ,
594
696
unit. name_ver( ) ,
595
697
unit. target,
596
698
unit. duration,
597
- codegen,
598
- features,
599
699
) ?;
600
700
}
601
701
write ! ( f, "</tbody>\n </table>\n " ) ?;
@@ -604,15 +704,6 @@ impl<'gctx> Timings<'gctx> {
604
704
}
605
705
606
706
impl UnitTime {
607
- /// Returns the codegen time as (`rmeta_time`, `codegen_time`, percent of total)
608
- fn codegen_time ( & self ) -> Option < ( f64 , f64 , f64 ) > {
609
- self . rmeta_time . map ( |rmeta_time| {
610
- let ctime = self . duration - rmeta_time;
611
- let cent = ( ctime / self . duration ) * 100.0 ;
612
- ( rmeta_time, ctime, cent)
613
- } )
614
- }
615
-
616
707
fn name_ver ( & self ) -> String {
617
708
format ! ( "{} v{}" , self . unit. pkg. name( ) , self . unit. pkg. version( ) )
618
709
}
0 commit comments