1
- use crate :: cli:: { parse_cli, Args , BenchmarkArgs } ;
1
+ use crate :: cli:: { parse_cli, Args , BenchmarkArgs , ProfileArgs } ;
2
2
use crate :: comm:: messages:: { BenchmarkMessage , BenchmarkResult , BenchmarkStats } ;
3
3
use crate :: comm:: output_message;
4
4
use crate :: measure:: benchmark_function;
5
5
use crate :: process:: raise_process_priority;
6
+ use crate :: profile:: profile_function;
6
7
use std:: collections:: HashMap ;
8
+ use std:: rc:: Rc ;
7
9
8
10
/// Create and run a new benchmark group. Use the closure argument to register
9
11
/// the individual benchmarks.
@@ -18,12 +20,21 @@ where
18
20
group. run ( ) . expect ( "Benchmark group execution has failed" ) ;
19
21
}
20
22
21
- /// Type-erased function that executes a single benchmark.
23
+ /// Type-erased function that executes a single benchmark and measures counter and wall-time
24
+ /// metrics.
22
25
type BenchmarkFn < ' a > = Box < dyn Fn ( ) -> anyhow:: Result < BenchmarkStats > + ' a > ;
23
26
27
+ /// Type-erased function that executes a single benchmark once.
28
+ type ProfileFn < ' a > = Box < dyn Fn ( ) + ' a > ;
29
+
30
+ struct BenchmarkFunctions < ' a > {
31
+ benchmark_fn : BenchmarkFn < ' a > ,
32
+ profile_fn : ProfileFn < ' a > ,
33
+ }
34
+
24
35
#[ derive( Default ) ]
25
36
pub struct BenchmarkGroup < ' a > {
26
- benchmarks : HashMap < & ' static str , BenchmarkFn < ' a > > ,
37
+ benchmarks : HashMap < & ' static str , BenchmarkFunctions < ' a > > ,
27
38
}
28
39
29
40
impl < ' a > BenchmarkGroup < ' a > {
@@ -40,8 +51,13 @@ impl<'a> BenchmarkGroup<'a> {
40
51
Bench : FnOnce ( ) -> R ,
41
52
{
42
53
// We want to type-erase the target `func` by wrapping it in a Box.
43
- let benchmark_fn = Box :: new ( move || benchmark_function ( & constructor) ) ;
44
- if self . benchmarks . insert ( name, benchmark_fn) . is_some ( ) {
54
+ let constructor = Rc :: new ( constructor) ;
55
+ let constructor2 = constructor. clone ( ) ;
56
+ let benchmark_fns = BenchmarkFunctions {
57
+ benchmark_fn : Box :: new ( move || benchmark_function ( constructor. as_ref ( ) ) ) ,
58
+ profile_fn : Box :: new ( move || profile_function ( constructor2. as_ref ( ) ) ) ,
59
+ } ;
60
+ if self . benchmarks . insert ( name, benchmark_fns) . is_some ( ) {
45
61
panic ! ( "Benchmark '{}' was registered twice" , name) ;
46
62
}
47
63
}
@@ -56,14 +72,15 @@ impl<'a> BenchmarkGroup<'a> {
56
72
Args :: Run ( args) => {
57
73
self . run_benchmarks ( args) ?;
58
74
}
75
+ Args :: Profile ( args) => self . profile_benchmark ( args) ?,
59
76
Args :: List => self . list_benchmarks ( ) ?,
60
77
}
61
78
62
79
Ok ( ( ) )
63
80
}
64
81
65
82
fn run_benchmarks ( self , args : BenchmarkArgs ) -> anyhow:: Result < ( ) > {
66
- let mut items: Vec < ( & ' static str , BenchmarkFn ) > = self
83
+ let mut items: Vec < ( & ' static str , BenchmarkFunctions ) > = self
67
84
. benchmarks
68
85
. into_iter ( )
69
86
. filter ( |( name, _) | {
@@ -74,17 +91,17 @@ impl<'a> BenchmarkGroup<'a> {
74
91
75
92
let mut stdout = std:: io:: stdout ( ) . lock ( ) ;
76
93
77
- for ( name, benchmark_fn ) in items {
94
+ for ( name, benchmark_fns ) in items {
78
95
let mut stats: Vec < BenchmarkStats > = Vec :: with_capacity ( args. iterations as usize ) ;
79
96
// Warm-up
80
97
for _ in 0 ..3 {
81
- let benchmark_stats = benchmark_fn ( ) ?;
98
+ let benchmark_stats = ( benchmark_fns . benchmark_fn ) ( ) ?;
82
99
black_box ( benchmark_stats) ;
83
100
}
84
101
85
102
// Actual measurement
86
103
for i in 0 ..args. iterations {
87
- let benchmark_stats = benchmark_fn ( ) ?;
104
+ let benchmark_stats = ( benchmark_fns . benchmark_fn ) ( ) ?;
88
105
log:: info!( "Benchmark (run {i}) `{name}` completed: {benchmark_stats:?}" ) ;
89
106
stats. push ( benchmark_stats) ;
90
107
}
@@ -100,6 +117,16 @@ impl<'a> BenchmarkGroup<'a> {
100
117
Ok ( ( ) )
101
118
}
102
119
120
+ fn profile_benchmark ( self , args : ProfileArgs ) -> anyhow:: Result < ( ) > {
121
+ let Some ( benchmark) = self . benchmarks . get ( args. benchmark . as_str ( ) ) else {
122
+ return Err ( anyhow:: anyhow!( "Benchmark `{}` not found. Available benchmarks: {}" , args. benchmark,
123
+ self . benchmarks. keys( ) . map( |s| s. to_string( ) ) . collect:: <Vec <_>>( ) . join( ", " ) ) ) ;
124
+ } ;
125
+ ( benchmark. profile_fn ) ( ) ;
126
+
127
+ Ok ( ( ) )
128
+ }
129
+
103
130
fn list_benchmarks ( self ) -> anyhow:: Result < ( ) > {
104
131
let benchmark_list: Vec < & str > = self . benchmarks . into_keys ( ) . collect ( ) ;
105
132
serde_json:: to_writer ( std:: io:: stdout ( ) , & benchmark_list) ?;
0 commit comments