11use crate :: keys:: KeyBuilder ;
22use anyhow:: Result ;
33use sightglass_data:: { EffectSize , Measurement , Phase , Summary } ;
4- use std:: { collections:: BTreeSet , io:: Write } ;
4+ use std:: { borrow :: Cow , collections:: BTreeSet , io:: Write } ;
55
66/// Find the effect size (and confidence interval) of between two different
77/// engines (i.e. two different commits of Wasmtime).
@@ -25,14 +25,20 @@ pub fn calculate<'a>(
2525 significance_level,
2626 ) ;
2727
28- let keys = KeyBuilder :: all ( ) . engine ( false ) . keys ( measurements) ;
28+ let keys = KeyBuilder :: all ( )
29+ . engine ( false )
30+ . engine_flags ( false )
31+ . keys ( measurements) ;
2932 let mut results = Vec :: with_capacity ( keys. len ( ) ) ;
3033
3134 for key in keys {
3235 let key_measurements: Vec < _ > = measurements. iter ( ) . filter ( |m| key. matches ( m) ) . collect ( ) ;
3336
3437 // NB: `BTreeSet` so they're always sorted.
35- let engines: BTreeSet < _ > = key_measurements. iter ( ) . map ( |m| & m. engine ) . collect ( ) ;
38+ let engines: BTreeSet < _ > = key_measurements
39+ . iter ( )
40+ . map ( |m| ( & m. engine , & m. engine_flags ) )
41+ . collect ( ) ;
3642 anyhow:: ensure!(
3743 engines. len( ) == 2 ,
3844 "Can only test significance between exactly two different engines. Found {} \
@@ -41,17 +47,17 @@ pub fn calculate<'a>(
4147 ) ;
4248
4349 let mut engines = engines. into_iter ( ) ;
44- let engine_a = engines. next ( ) . unwrap ( ) ;
45- let engine_b = engines. next ( ) . unwrap ( ) ;
50+ let ( engine_a, engine_a_flags ) = engines. next ( ) . unwrap ( ) ;
51+ let ( engine_b, engine_b_flags ) = engines. next ( ) . unwrap ( ) ;
4652
4753 let a: behrens_fisher:: Stats = key_measurements
4854 . iter ( )
49- . filter ( |m| m. engine . as_ref ( ) == engine_a)
55+ . filter ( |m| m. engine . as_ref ( ) == engine_a && & m . engine_flags == engine_a_flags )
5056 . map ( |m| m. count as f64 )
5157 . collect ( ) ;
5258 let b: behrens_fisher:: Stats = key_measurements
5359 . iter ( )
54- . filter ( |m| m. engine . as_ref ( ) == engine_b)
60+ . filter ( |m| m. engine . as_ref ( ) == engine_b && & m . engine_flags == engine_b_flags )
5561 . map ( |m| m. count as f64 )
5662 . collect ( ) ;
5763
@@ -62,8 +68,10 @@ pub fn calculate<'a>(
6268 phase : key. phase . unwrap ( ) ,
6369 event : key. event . unwrap ( ) ,
6470 a_engine : engine_a. clone ( ) ,
71+ a_engine_flags : engine_a_flags. clone ( ) ,
6572 a_mean : a. mean ,
6673 b_engine : engine_b. clone ( ) ,
74+ b_engine_flags : engine_b_flags. clone ( ) ,
6775 b_mean : b. mean ,
6876 significance_level,
6977 half_width_confidence_interval : ci,
@@ -73,6 +81,18 @@ pub fn calculate<'a>(
7381 Ok ( results)
7482}
7583
84+ fn engine_label ( engine : & str , engine_flags : & Option < Cow < str > > ) -> String {
85+ format ! (
86+ "{}{}" ,
87+ engine,
88+ if let Some ( ef) = engine_flags {
89+ format!( " ({ef})" )
90+ } else {
91+ "" . into( )
92+ }
93+ )
94+ }
95+
7696/// Write a vector of [EffectSize] structures to the passed `output_file` in human-readable form.
7797/// The `summaries` are needed
7898pub fn write (
@@ -100,22 +120,50 @@ pub fn write(
100120 ) ?;
101121 writeln ! ( output_file) ?;
102122
123+ let end_of_shared_prefix = |astr : & str , bstr : & str | {
124+ astr. char_indices ( )
125+ . zip ( bstr. char_indices ( ) )
126+ . find_map ( |( ( i, a) , ( j, b) ) | {
127+ if a == b {
128+ None
129+ } else {
130+ debug_assert_eq ! ( i, j) ;
131+ Some ( i)
132+ }
133+ } )
134+ . unwrap_or ( 0 )
135+ } ;
136+
103137 // For readability, trim the shared prefix from our two engine names.
104- let end_of_shared_prefix = effect_size
105- . a_engine
106- . char_indices ( )
107- . zip ( effect_size. b_engine . char_indices ( ) )
108- . find_map ( |( ( i, a) , ( j, b) ) | {
109- if a == b {
110- None
111- } else {
112- debug_assert_eq ! ( i, j) ;
113- Some ( i)
114- }
115- } )
116- . unwrap_or ( 0 ) ;
117- let a_engine = & effect_size. a_engine [ end_of_shared_prefix..] ;
118- let b_engine = & effect_size. b_engine [ end_of_shared_prefix..] ;
138+ //
139+ // Furthermore, there are a few special cases:
140+ // 1. If the engines are the same, show just the flags.
141+ // 2. If not, show the computed full label with common prefix removed.
142+ let ( a_eng_label, b_eng_label) = if effect_size. a_engine == effect_size. b_engine {
143+ (
144+ effect_size
145+ . a_engine_flags
146+ . as_ref ( )
147+ . map ( |ref ef| ef. to_string ( ) )
148+ . unwrap_or_else ( || "(no flags)" . into ( ) )
149+ . to_string ( ) ,
150+ effect_size
151+ . b_engine_flags
152+ . as_ref ( )
153+ . map ( |ref ef| ef. to_string ( ) )
154+ . unwrap_or_else ( || "(no flags)" . into ( ) )
155+ . to_string ( ) ,
156+ )
157+ } else {
158+ let a_label = engine_label ( & effect_size. a_engine , & effect_size. a_engine_flags ) ;
159+ let b_label = engine_label ( & effect_size. b_engine , & effect_size. b_engine_flags ) ;
160+ let idx_end_of_shared = end_of_shared_prefix ( & a_label, & b_label) ;
161+
162+ (
163+ a_label[ idx_end_of_shared..] . into ( ) ,
164+ b_label[ idx_end_of_shared..] . into ( ) ,
165+ )
166+ } ;
119167
120168 if effect_size. is_significant ( ) {
121169 writeln ! (
@@ -132,9 +180,7 @@ pub fn write(
132180 let ratio_ci = effect_size. half_width_confidence_interval / effect_size. a_mean ;
133181 writeln ! (
134182 output_file,
135- " {a_engine} is {ratio_min:.2}x to {ratio_max:.2}x faster than {b_engine}!" ,
136- a_engine = a_engine,
137- b_engine = b_engine,
183+ " {a_eng_label} is {ratio_min:.2}x to {ratio_max:.2}x faster than {b_eng_label}!" ,
138184 ratio_min = ratio - ratio_ci,
139185 ratio_max = ratio + ratio_ci,
140186 ) ?;
@@ -143,9 +189,7 @@ pub fn write(
143189 let ratio_ci = effect_size. half_width_confidence_interval / effect_size. b_mean ;
144190 writeln ! (
145191 output_file,
146- " {b_engine} is {ratio_min:.2}x to {ratio_max:.2}x faster than {a_engine}!" ,
147- a_engine = a_engine,
148- b_engine = b_engine,
192+ " {b_eng_label} is {ratio_min:.2}x to {ratio_max:.2}x faster than {a_eng_label}!" ,
149193 ratio_min = ratio - ratio_ci,
150194 ratio_max = ratio + ratio_ci,
151195 ) ?;
@@ -155,39 +199,49 @@ pub fn write(
155199 }
156200 writeln ! ( output_file) ?;
157201
158- let get_summary = |engine : & str , wasm : & str , phase : Phase , event : & str | {
202+ let get_summary = |engine : & str ,
203+ engine_flags : Option < Cow < str > > ,
204+ wasm : & str ,
205+ phase : Phase ,
206+ event : & str | {
159207 // TODO this sorting is not using `arch` which is not guaranteed to be the same in
160208 // result sets; potentially this could re-use `Key` functionality.
161209 summaries
162210 . iter ( )
163211 . find ( |s| {
164- s. engine == engine && s. wasm == wasm && s. phase == phase && s. event == event
212+ s. engine == engine
213+ && s. engine_flags == engine_flags
214+ && s. wasm == wasm
215+ && s. phase == phase
216+ && s. event == event
165217 } )
166218 . unwrap ( )
167219 } ;
168220
169221 let a_summary = get_summary (
170222 & effect_size. a_engine ,
223+ effect_size. a_engine_flags ,
171224 & effect_size. wasm ,
172225 effect_size. phase ,
173226 & effect_size. event ,
174227 ) ;
175228 writeln ! (
176229 output_file,
177230 " [{} {:.2} {}] {}" ,
178- a_summary. min, a_summary. mean, a_summary. max, a_engine ,
231+ a_summary. min, a_summary. mean, a_summary. max, a_eng_label ,
179232 ) ?;
180233
181234 let b_summary = get_summary (
182235 & effect_size. b_engine ,
236+ effect_size. b_engine_flags ,
183237 & effect_size. wasm ,
184238 effect_size. phase ,
185239 & effect_size. event ,
186240 ) ;
187241 writeln ! (
188242 output_file,
189243 " [{} {:.2} {}] {}" ,
190- b_summary. min, b_summary. mean, b_summary. max, b_engine ,
244+ b_summary. min, b_summary. mean, b_summary. max, b_eng_label ,
191245 ) ?;
192246 }
193247
0 commit comments