11use crate :: cli:: { parse_cli, Args , BenchmarkArgs } ;
2+ use crate :: comm:: messages:: { BenchmarkMessage , BenchmarkResult , BenchmarkStats } ;
3+ use crate :: comm:: output_message;
24use crate :: measure:: benchmark_function;
3- use crate :: messages:: BenchmarkResult ;
45use crate :: process:: raise_process_priority;
5- use log:: LevelFilter ;
66use std:: collections:: HashMap ;
77
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" ) ;
1615}
1716
18- /// Type-erased function that performs a benchmark.
17+ /// Type-erased function that executes a single benchmark.
1918struct BenchmarkWrapper {
20- func : Box < dyn Fn ( ) -> anyhow:: Result < BenchmarkResult > > ,
19+ func : Box < dyn Fn ( ) -> anyhow:: Result < BenchmarkStats > > ,
2120}
2221
23- type BenchmarkMap = HashMap < & ' static str , BenchmarkWrapper > ;
24-
2522#[ derive( Default ) ]
26- pub struct BenchmarkSuite {
27- benchmarks : BenchmarkMap ,
23+ pub struct BenchmarkGroup {
24+ benchmarks : HashMap < & ' static str , BenchmarkWrapper > ,
2825}
2926
30- impl BenchmarkSuite {
27+ impl BenchmarkGroup {
3128 pub fn new ( ) -> Self {
3229 Self :: default ( )
3330 }
3431
3532 /// Registers a single benchmark.
36- /// `func ` should return a closure that will be benchmarked.
33+ /// `constructor ` should return a closure that will be benchmarked.
3734 pub fn register < F : Fn ( ) -> Bench + Clone + ' static , R , Bench : FnOnce ( ) -> R + ' static > (
3835 & mut self ,
3936 name : & ' static str ,
4037 constructor : F ,
4138 ) {
4239 // 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 ( ) ) ) ;
4441 let benchmark_def = BenchmarkWrapper {
4542 func : benchmark_func,
4643 } ;
@@ -49,62 +46,81 @@ impl BenchmarkSuite {
4946 }
5047 }
5148
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
5350 /// them.
5451 pub fn run ( self ) -> anyhow:: Result < ( ) > {
5552 raise_process_priority ( ) ;
5653
5754 let args = parse_cli ( ) ?;
5855 match args {
59- Args :: Benchmark ( args) => {
60- run_benchmark ( args , self . benchmarks ) ?;
56+ Args :: Run ( args) => {
57+ self . run_benchmarks ( args ) ?;
6158 }
59+ Args :: List => self . list_benchmarks ( ) ?,
6260 }
6361
6462 Ok ( ( ) )
6563 }
66- }
6764
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+ ) ?;
8191 }
92+
93+ Ok ( ( ) )
8294 }
8395
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+ }
86102}
87103
88- /// Adds a single benchmark to the benchmark suite .
104+ /// Adds a single benchmark to the benchmark group .
89105/// ```ignore
90106/// use benchlib::define_benchmark;
91107///
92- /// define_benchmark!(suite , my_bench, {
108+ /// define_benchmark!(group , my_bench, {
93109/// || do_something()
94110/// });
95111/// ```
96112#[ macro_export]
97113macro_rules! define_benchmark {
98- ( $suite : expr, $name: ident, $fun: expr) => {
114+ ( $group : expr, $name: ident, $fun: expr) => {
99115 let func = move || $fun;
100- $suite . register( stringify!( $name) , func) ;
116+ $group . register( stringify!( $name) , func) ;
101117 } ;
102118}
103119
104120pub use define_benchmark;
105121
106122/// 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 {
108124 match ( exclude, include) {
109125 ( Some ( exclude) , Some ( include) ) => name. starts_with ( include) && !name. starts_with ( exclude) ,
110126 ( None , Some ( include) ) => name. starts_with ( include) ,
0 commit comments