@@ -32,6 +32,10 @@ struct Args {
32
32
/// path to line-delimited capture file
33
33
capture_path : String ,
34
34
35
+ /// path to a second capture file for comparison
36
+ #[ clap( short, long) ]
37
+ comparison_capture_path : Option < String > ,
38
+
35
39
/// warmup period in seconds - data points before this time will be ignored
36
40
#[ clap( long) ]
37
41
warmup : Option < u64 > ,
@@ -47,20 +51,15 @@ pub enum Error {
47
51
Deserialize ( #[ from] serde_json:: Error ) ,
48
52
}
49
53
50
- #[ tokio:: main( flavor = "current_thread" ) ]
51
- async fn main ( ) -> Result < ( ) , Error > {
52
- tracing_subscriber:: fmt ( )
53
- . with_span_events ( FmtSpan :: FULL )
54
- . with_ansi ( false )
55
- . finish ( )
56
- . init ( ) ;
57
-
58
- info ! ( "Welcome to captool" ) ;
59
- let args = Args :: parse ( ) ;
54
+ concatenate ! ( Estimator , [ Variance , mean] , [ Max , max] , [ Min , min] ) ;
60
55
61
- let capture_path = std:: path:: Path :: new ( & args. capture_path ) ;
56
+ async fn process_capture_path (
57
+ args : & Args ,
58
+ path : & str ,
59
+ ) -> Result < Option < Vec < ( String , Estimator ) > > , Error > {
60
+ let capture_path = std:: path:: Path :: new ( & path) ;
62
61
if !capture_path. exists ( ) {
63
- error ! ( "Capture file {} does not exist" , & args . capture_path ) ;
62
+ error ! ( "Capture file {path } does not exist" ) ;
64
63
return Err ( Error :: InvalidArgs ) ;
65
64
}
66
65
@@ -106,7 +105,7 @@ async fn main() -> Result<(), Error> {
106
105
for ( name, kind) in names {
107
106
println ! ( "{kind}: {name}" ) ;
108
107
}
109
- return Ok ( ( ) ) ;
108
+ return Ok ( None ) ;
110
109
}
111
110
112
111
if let Some ( metric) = & args. metric {
@@ -155,26 +154,109 @@ async fn main() -> Result<(), Error> {
155
154
entry. 1 . push ( line. value . as_f64 ( ) ) ;
156
155
}
157
156
158
- concatenate ! ( Estimator , [ Variance , mean] , [ Max , max] , [ Min , min] ) ;
159
- let is_monotonic = |v : & Vec < _ > | v. windows ( 2 ) . all ( |w| w[ 0 ] <= w[ 1 ] ) ;
157
+ // let is_monotonic = |v: &Vec<_>| v.windows(2).all(|w| w[0] <= w[1]);
158
+
159
+ let mut estimators = vec ! [ ] ;
160
160
161
161
for ( _, ( labels, values) ) in context_map. iter ( ) {
162
162
let s: Estimator = values. iter ( ) . copied ( ) . collect ( ) ;
163
- info ! (
164
- "{metric}[{labels}]: min: {}, mean: {}, max: {}, is_monotonic: {}" ,
165
- s. min( ) ,
166
- s. mean( ) ,
167
- s. max( ) ,
168
- is_monotonic( values) ,
169
- labels = labels. iter( ) . cloned( ) . collect:: <Vec <String >>( ) . join( "," )
170
- ) ;
163
+ // println!(
164
+ // "{metric}[{labels}]: min: {}, mean: {}, max: {}, is_monotonic: {}",
165
+ // s.min(),
166
+ // s.mean(),
167
+ // s.max(),
168
+ // is_monotonic(values),
169
+ // labels = labels.iter().cloned().collect::<Vec<String>>().join(",")
170
+ // );
171
+
172
+ let mut context = labels
173
+ . iter ( )
174
+ . filter ( |v| !v. starts_with ( "replicate:" ) && !v. starts_with ( "pid:" ) )
175
+ . cloned ( )
176
+ . collect :: < Vec < _ > > ( ) ;
177
+ context. sort ( ) ;
178
+ let context = context. join ( "," ) ;
179
+
180
+ estimators. push ( ( context, s) ) ;
171
181
}
172
182
173
183
if args. dump_values {
174
184
for line in & filtered {
175
185
println ! ( "{}: {}" , line. fetch_index, line. value) ;
176
186
}
177
187
}
188
+
189
+ return Ok ( Some ( estimators) ) ;
190
+ }
191
+
192
+ Ok ( None )
193
+ }
194
+
195
+ #[ tokio:: main( flavor = "current_thread" ) ]
196
+ async fn main ( ) -> Result < ( ) , Error > {
197
+ tracing_subscriber:: fmt ( )
198
+ . with_env_filter ( tracing_subscriber:: EnvFilter :: from_default_env ( ) )
199
+ . with_span_events ( FmtSpan :: FULL )
200
+ . with_ansi ( false )
201
+ . finish ( )
202
+ . init ( ) ;
203
+
204
+ info ! ( "Welcome to captool" ) ;
205
+ let args = Args :: parse ( ) ;
206
+
207
+ let cap1 = process_capture_path ( & args, & args. capture_path ) . await ?;
208
+
209
+ if let Some ( comparison_capture_path) = & args. comparison_capture_path {
210
+ let metric = args. metric . clone ( ) . unwrap ( ) ;
211
+
212
+ let cap1 = cap1. unwrap ( ) ;
213
+ let cap2 = process_capture_path ( & args, comparison_capture_path)
214
+ . await ?
215
+ . unwrap ( ) ;
216
+
217
+ let all_contexts = cap1
218
+ . iter ( )
219
+ . map ( |( labels, _) | labels. clone ( ) )
220
+ . chain ( cap2. iter ( ) . map ( |( labels, _) | labels. clone ( ) ) )
221
+ . collect :: < BTreeSet < _ > > ( ) ;
222
+
223
+ for label in & all_contexts {
224
+ let cap1_lookup = & cap1. iter ( ) . find ( |( labels, _) | labels. contains ( label) ) ;
225
+ let cap2_lookup = & cap2. iter ( ) . find ( |( labels, _) | labels. contains ( label) ) ;
226
+
227
+ if cap1_lookup. is_none ( ) {
228
+ println ! ( "{} only present in cap2" , label) ;
229
+ continue ;
230
+ }
231
+ if cap2_lookup. is_none ( ) {
232
+ println ! ( "{} only present in cap1" , label) ;
233
+ continue ;
234
+ }
235
+
236
+ let cap1_est = & cap1_lookup. unwrap ( ) . 1 ;
237
+ let cap2_est = & cap2_lookup. unwrap ( ) . 1 ;
238
+
239
+ let min = cap1_est. min ( ) - cap2_est. min ( ) ;
240
+ let mean = cap1_est. mean ( ) - cap2_est. mean ( ) ;
241
+ let max = cap1_est. max ( ) - cap2_est. max ( ) ;
242
+
243
+ if min. abs ( ) > 0.01 || mean. abs ( ) > 0.01 || max. abs ( ) > 0.01 {
244
+ println ! ( "{metric}[{label}]" ) ;
245
+ println ! (
246
+ "\t A: min: {}, mean: {}, max: {}" ,
247
+ cap1_est. min( ) ,
248
+ cap1_est. mean( ) ,
249
+ cap1_est. max( ) ,
250
+ ) ;
251
+ println ! (
252
+ "\t B: min: {}, mean: {}, max: {}" ,
253
+ cap2_est. min( ) ,
254
+ cap2_est. mean( ) ,
255
+ cap2_est. max( ) ,
256
+ ) ;
257
+ println ! ( "\t Diff min: {min}, mean: {mean}, max: {max}" ) ;
258
+ }
259
+ }
178
260
}
179
261
180
262
info ! ( "Bye. :)" ) ;
0 commit comments