@@ -19,10 +19,12 @@ use anyhow::Context;
19
19
use clap:: builder:: TypedValueParser ;
20
20
use clap:: { Arg , Parser } ;
21
21
use humansize:: { format_size, BINARY } ;
22
+ use itertools:: Itertools ;
22
23
use rayon:: iter:: { IndexedParallelIterator , IntoParallelRefIterator , ParallelIterator } ;
23
24
use tabled:: builder:: Builder ;
24
25
use tabled:: settings:: object:: { Columns , Rows } ;
25
26
use tabled:: settings:: { Alignment , Border , Color , Modify } ;
27
+ use tabled:: { Table , Tabled } ;
26
28
use tokio:: runtime:: Runtime ;
27
29
28
30
use collector:: api:: next_artifact:: NextArtifact ;
@@ -52,7 +54,7 @@ use collector::toolchain::{
52
54
use collector:: utils:: cachegrind:: cachegrind_diff;
53
55
use collector:: utils:: { is_installed, wait_for_future} ;
54
56
use collector:: { utils, CollectorCtx , CollectorStepBuilder } ;
55
- use database:: { ArtifactId , ArtifactIdNumber , Commit , CommitType , Connection , Pool } ;
57
+ use database:: { ArtifactId , ArtifactIdNumber , Commit , CommitType , Connection , Lookup , Pool } ;
56
58
57
59
fn n_normal_benchmarks_remaining ( n : usize ) -> String {
58
60
let suffix = if n == 1 { "" } else { "s" } ;
@@ -628,6 +630,18 @@ enum Commands {
628
630
#[ command( flatten) ]
629
631
db : DbOption ,
630
632
} ,
633
+
634
+ /// Displays diff between two local bench result.
635
+ BenchLocalDiff {
636
+ #[ command( flatten) ]
637
+ db : DbOption ,
638
+
639
+ #[ arg( long) ]
640
+ a_id : String ,
641
+
642
+ #[ arg( long) ]
643
+ b_id : String ,
644
+ } ,
631
645
}
632
646
633
647
#[ derive( Debug , clap:: Parser ) ]
@@ -1187,6 +1201,105 @@ Make sure to modify `{dir}/perf-config.json` if the category/artifact don't matc
1187
1201
println ! ( "Data of artifact {name} were removed" ) ;
1188
1202
Ok ( 0 )
1189
1203
}
1204
+ Commands :: BenchLocalDiff { db, a_id, b_id } => {
1205
+ let pool = Pool :: open ( & db. db ) ;
1206
+ let rt = build_async_runtime ( ) ;
1207
+ let mut conn = rt. block_on ( pool. connection ( ) ) ;
1208
+ let index = rt. block_on ( database:: Index :: load ( & mut * conn) ) ;
1209
+
1210
+ let sids = index
1211
+ . compile_statistic_descriptions ( )
1212
+ . map ( |( _, sid) | sid)
1213
+ . collect :: < Vec < _ > > ( ) ;
1214
+
1215
+ let a_id_number = rt
1216
+ . block_on ( conn. artifact_by_name ( & a_id) )
1217
+ . expect ( "Cannot find specified artifact" )
1218
+ . lookup ( & index)
1219
+ . unwrap ( ) ;
1220
+ let b_id_number = rt
1221
+ . block_on ( conn. artifact_by_name ( & b_id) )
1222
+ . expect ( "Cannot find specified artifact" )
1223
+ . lookup ( & index)
1224
+ . unwrap ( ) ;
1225
+
1226
+ let pstats =
1227
+ rt. block_on ( conn. get_pstats ( & sids, & [ Some ( a_id_number) , Some ( b_id_number) ] ) ) ;
1228
+ let tuple_pstats = pstats
1229
+ . into_iter ( )
1230
+ . map ( |row| row. into_iter ( ) . collect_tuple :: < ( _ , _ ) > ( ) . unwrap ( ) )
1231
+ . collect :: < Vec < ( Option < f64 > , Option < f64 > ) > > ( ) ;
1232
+
1233
+ #[ derive( Tabled ) ]
1234
+ struct Regression {
1235
+ count : usize ,
1236
+ #[ tabled( display_with = "display_range" ) ]
1237
+ range : ( f64 , f64 ) ,
1238
+ #[ tabled( display_with = "display_mean" ) ]
1239
+ mean : f64 ,
1240
+ }
1241
+
1242
+ fn display_range ( value : & ( f64 , f64 ) ) -> String {
1243
+ format ! ( "[{:+.2}%, {:+.2}%]" , value. 0 , value. 1 )
1244
+ }
1245
+
1246
+ fn display_mean ( value : & f64 ) -> String {
1247
+ format ! ( "{:+.2}%" , value)
1248
+ }
1249
+
1250
+ impl From < & Vec < f64 > > for Regression {
1251
+ fn from ( value : & Vec < f64 > ) -> Self {
1252
+ let min = * value. iter ( ) . min_by ( |a, b| a. total_cmp ( b) ) . unwrap ( ) ;
1253
+ let max = * value. iter ( ) . max_by ( |a, b| a. total_cmp ( b) ) . unwrap ( ) ;
1254
+ let count = value. len ( ) ;
1255
+
1256
+ Regression {
1257
+ range : ( min, max) ,
1258
+ count,
1259
+ mean : ( value. iter ( ) . sum :: < f64 > ( ) / count as f64 ) * 100.0 ,
1260
+ }
1261
+ }
1262
+ }
1263
+
1264
+ let change = tuple_pstats
1265
+ . iter ( )
1266
+ . filter_map ( |& ( a, b) | match ( a, b) {
1267
+ ( Some ( a) , Some ( b) ) => Some ( if a == 0.0 { 0.0 } else { ( b - a) / a } ) ,
1268
+ ( _, _) => None ,
1269
+ } )
1270
+ . collect :: < Vec < _ > > ( ) ;
1271
+ let negative_change = change
1272
+ . iter ( )
1273
+ . copied ( )
1274
+ . filter ( |& c| c < 0.0 )
1275
+ . collect :: < Vec < _ > > ( ) ;
1276
+ let positive_change = change
1277
+ . iter ( )
1278
+ . copied ( )
1279
+ . filter ( |& c| c > 0.0 )
1280
+ . collect :: < Vec < _ > > ( ) ;
1281
+
1282
+ #[ derive( Tabled ) ]
1283
+ struct NamedRegression {
1284
+ name : String ,
1285
+ #[ tabled( inline) ]
1286
+ regression : Regression ,
1287
+ }
1288
+
1289
+ let regressions = [ negative_change, positive_change, change]
1290
+ . into_iter ( )
1291
+ . map ( |c| Regression :: from ( & c) )
1292
+ . zip ( [ "❌" , "✅" , "✅, ❌" ] )
1293
+ . map ( |( c, label) | NamedRegression {
1294
+ name : label. to_string ( ) ,
1295
+ regression : c,
1296
+ } )
1297
+ . collect :: < Vec < _ > > ( ) ;
1298
+
1299
+ println ! ( "{}" , Table :: new( regressions) ) ;
1300
+
1301
+ Ok ( 0 )
1302
+ }
1190
1303
}
1191
1304
}
1192
1305
@@ -1736,7 +1849,6 @@ fn bench_compile(
1736
1849
category,
1737
1850
) ) ;
1738
1851
print_intro ( ) ;
1739
-
1740
1852
let mut processor = BenchProcessor :: new (
1741
1853
tx. conn ( ) ,
1742
1854
benchmark_name,
0 commit comments