1
1
use crate :: cli:: { parse_cli, Args , BenchmarkArgs } ;
2
+ use crate :: comm:: messages:: { BenchmarkMessage , BenchmarkResult , BenchmarkStats } ;
3
+ use crate :: comm:: output_message;
2
4
use crate :: measure:: benchmark_function;
3
- use crate :: messages:: BenchmarkResult ;
4
5
use crate :: process:: raise_process_priority;
5
- use log:: LevelFilter ;
6
6
use std:: collections:: HashMap ;
7
7
8
- /// Create a new benchmark suite. Use the closure argument to define benchmarks.
9
- pub fn benchmark_suite < F : FnOnce ( & mut BenchmarkSuite ) > ( define_func : F ) {
10
- env_logger:: Builder :: from_default_env ( )
11
- . filter_level ( LevelFilter :: Info )
12
- . init ( ) ;
13
- let mut suite = BenchmarkSuite :: new ( ) ;
14
- define_func ( & mut suite) ;
15
- suite. run ( ) . expect ( "Benchmark suite has failed" ) ;
8
+ /// Create a new benchmark group. Use the closure argument to define individual benchmarks.
9
+ pub fn run_benchmark_group < F : FnOnce ( & mut BenchmarkGroup ) > ( define_func : F ) {
10
+ env_logger:: init ( ) ;
11
+
12
+ let mut group = BenchmarkGroup :: new ( ) ;
13
+ define_func ( & mut group) ;
14
+ group. run ( ) . expect ( "Benchmark group execution has failed" ) ;
16
15
}
17
16
18
- /// Type-erased function that performs a benchmark.
17
+ /// Type-erased function that executes a single benchmark.
19
18
struct BenchmarkWrapper {
20
- func : Box < dyn Fn ( ) -> anyhow:: Result < BenchmarkResult > > ,
19
+ func : Box < dyn Fn ( ) -> anyhow:: Result < BenchmarkStats > > ,
21
20
}
22
21
23
- type BenchmarkMap = HashMap < & ' static str , BenchmarkWrapper > ;
24
-
25
22
#[ derive( Default ) ]
26
- pub struct BenchmarkSuite {
27
- benchmarks : BenchmarkMap ,
23
+ pub struct BenchmarkGroup {
24
+ benchmarks : HashMap < & ' static str , BenchmarkWrapper > ,
28
25
}
29
26
30
- impl BenchmarkSuite {
27
+ impl BenchmarkGroup {
31
28
pub fn new ( ) -> Self {
32
29
Self :: default ( )
33
30
}
34
31
35
32
/// Registers a single benchmark.
36
- /// `func ` should return a closure that will be benchmarked.
33
+ /// `constructor ` should return a closure that will be benchmarked.
37
34
pub fn register < F : Fn ( ) -> Bench + Clone + ' static , R , Bench : FnOnce ( ) -> R + ' static > (
38
35
& mut self ,
39
36
name : & ' static str ,
40
37
constructor : F ,
41
38
) {
42
39
// We want to type-erase the target `func` by wrapping it in a Box.
43
- let benchmark_func = Box :: new ( move || benchmark_function ( name , constructor. clone ( ) ) ) ;
40
+ let benchmark_func = Box :: new ( move || benchmark_function ( constructor. clone ( ) ) ) ;
44
41
let benchmark_def = BenchmarkWrapper {
45
42
func : benchmark_func,
46
43
} ;
@@ -49,62 +46,81 @@ impl BenchmarkSuite {
49
46
}
50
47
}
51
48
52
- /// Execute the benchmark suite . It will parse CLI arguments and decide what to do based on
49
+ /// Execute the benchmark group . It will parse CLI arguments and decide what to do based on
53
50
/// them.
54
51
pub fn run ( self ) -> anyhow:: Result < ( ) > {
55
52
raise_process_priority ( ) ;
56
53
57
54
let args = parse_cli ( ) ?;
58
55
match args {
59
- Args :: Benchmark ( args) => {
60
- run_benchmark ( args , self . benchmarks ) ?;
56
+ Args :: Run ( args) => {
57
+ self . run_benchmarks ( args ) ?;
61
58
}
59
+ Args :: List => self . list_benchmarks ( ) ?,
62
60
}
63
61
64
62
Ok ( ( ) )
65
63
}
66
- }
67
64
68
- fn run_benchmark ( args : BenchmarkArgs , benchmarks : BenchmarkMap ) -> anyhow:: Result < ( ) > {
69
- let mut items: Vec < ( & ' static str , BenchmarkWrapper ) > = benchmarks
70
- . into_iter ( )
71
- . filter ( |( name, _) | passes_filter ( name, args. exclude . as_deref ( ) , args. include . as_deref ( ) ) )
72
- . collect ( ) ;
73
- items. sort_unstable_by_key ( |item| item. 0 ) ;
74
-
75
- let mut results: Vec < BenchmarkResult > = Vec :: with_capacity ( items. len ( ) ) ;
76
- for ( name, def) in items {
77
- for i in 0 ..args. iterations {
78
- let result = ( def. func ) ( ) ?;
79
- log:: info!( "Benchmark (run {i}) `{name}` completed: {result:?}" ) ;
80
- results. push ( result) ;
65
+ fn run_benchmarks ( self , args : BenchmarkArgs ) -> anyhow:: Result < ( ) > {
66
+ let mut items: Vec < ( & ' static str , BenchmarkWrapper ) > = self
67
+ . benchmarks
68
+ . into_iter ( )
69
+ . filter ( |( name, _) | {
70
+ passes_filter ( name, args. exclude . as_deref ( ) , args. include . as_deref ( ) )
71
+ } )
72
+ . collect ( ) ;
73
+ items. sort_unstable_by_key ( |item| item. 0 ) ;
74
+
75
+ let mut stdout = std:: io:: stdout ( ) . lock ( ) ;
76
+
77
+ for ( name, def) in items {
78
+ let mut stats: Vec < BenchmarkStats > = Vec :: with_capacity ( args. iterations as usize ) ;
79
+ for i in 0 ..args. iterations {
80
+ let benchmark_stats = ( def. func ) ( ) ?;
81
+ log:: info!( "Benchmark (run {i}) `{name}` completed: {benchmark_stats:?}" ) ;
82
+ stats. push ( benchmark_stats) ;
83
+ }
84
+ output_message (
85
+ & mut stdout,
86
+ BenchmarkMessage :: Result ( BenchmarkResult {
87
+ name : name. to_string ( ) ,
88
+ stats,
89
+ } ) ,
90
+ ) ?;
81
91
}
92
+
93
+ Ok ( ( ) )
82
94
}
83
95
84
- println ! ( "{}" , serde_json:: to_string( & results) ?) ;
85
- Ok ( ( ) )
96
+ fn list_benchmarks ( self ) -> anyhow:: Result < ( ) > {
97
+ let benchmark_list: Vec < & str > = self . benchmarks . into_keys ( ) . collect ( ) ;
98
+ serde_json:: to_writer ( std:: io:: stdout ( ) , & benchmark_list) ?;
99
+
100
+ Ok ( ( ) )
101
+ }
86
102
}
87
103
88
- /// Adds a single benchmark to the benchmark suite .
104
+ /// Adds a single benchmark to the benchmark group .
89
105
/// ```ignore
90
106
/// use benchlib::define_benchmark;
91
107
///
92
- /// define_benchmark!(suite , my_bench, {
108
+ /// define_benchmark!(group , my_bench, {
93
109
/// || do_something()
94
110
/// });
95
111
/// ```
96
112
#[ macro_export]
97
113
macro_rules! define_benchmark {
98
- ( $suite : expr, $name: ident, $fun: expr) => {
114
+ ( $group : expr, $name: ident, $fun: expr) => {
99
115
let func = move || $fun;
100
- $suite . register( stringify!( $name) , func) ;
116
+ $group . register( stringify!( $name) , func) ;
101
117
} ;
102
118
}
103
119
104
120
pub use define_benchmark;
105
121
106
122
/// Tests if the name of the benchmark passes through the include and exclude filter flags.
107
- fn passes_filter ( name : & str , exclude : Option < & str > , include : Option < & str > ) -> bool {
123
+ pub fn passes_filter ( name : & str , exclude : Option < & str > , include : Option < & str > ) -> bool {
108
124
match ( exclude, include) {
109
125
( Some ( exclude) , Some ( include) ) => name. starts_with ( include) && !name. starts_with ( exclude) ,
110
126
( None , Some ( include) ) => name. starts_with ( include) ,
0 commit comments