ppbench-locked 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/bin/ppbench.rb ADDED
@@ -0,0 +1,1012 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'ppbench'
4
+ require 'commander/import'
5
+ require 'terminal-table'
6
+ require 'json'
7
+
8
+ # Description constants used by the help command.
9
+ #
10
+ MACHINES_DESCRIPTION = 'Consider only specific machines (e.g. m3.large,m3.xlarge); comma separated list.'
11
+ EXPERIMENTS_DESCRIPTION = 'Consider only specific experiments (e.g. bare,docker,weave); comma separated list.'
12
+ RECWINDOW_DESCRIPTION = 'Standard Receive Window. Defaults to 87380 byte (Window is not plotted if set to 0).'
13
+ YAXIS_MAX_DESCRIPTION = 'Maximum Y value on the Y axis (defaults to biggest value found).'
14
+ YAXIS_STEPS_DESCRIPTION = 'How many ticks shall be plotted on yaxis (defaults to 10).'
15
+ XAXIS_MAX_DESCRIPTION = 'Maximum X value on the X axis (defaults to biggest message size found).'
16
+ XAXIS_STEPS_DESCRIPTION = 'How many ticks shall be plotted on xaxis (defaults to 10).'
17
+ PRECISION_DESCRIPTION = 'Amount of points used per series for plotting medians, comparisons and confidence intervals.'
18
+ CONFIDENCE_DESCRIPTION = 'Percent value for confidence bands. Defaults to 90%.'
19
+ WITHBANDS_DESCRIPTION = 'Plots confidence bands (confidence bands are _not_ plotted by default).'
20
+ NOPOINTS_DESCRIPTION = 'Show no points (points are plotted by default).'
21
+ NAMING_DESCRIPTION = 'Use user defined names via an JSON file.'
22
+ ALPHA_DESCRIPTION = "Transparency (alpha) for points of scatter plots (defaults to 0.05, must be between 0.0 and 1.0)"
23
+ PDF_DESCRIPTION = 'Adds additional commands to an R script, so that it can be used to generate a PDF file.'
24
+ PDF_WIDTH_DESCRIPTION = 'Width of plot in inch (defaults to 7 inch, only useful with PDF output).'
25
+ PDF_HEIGHT_DESCRIPTION = 'Height of plot in inch (defaults to 7 inch, only useful with PDF output).'
26
+
27
+ # Plotting constants used for plotting.
28
+ #
29
+ RECWINDOW_DEFAULT = 87380
30
+ CONFIDENCE_DEFAULT = 90
31
+ AXIS_STEP_DEFAULT = 10
32
+ COMPARISON_MAX_DEFAULT = 2.0
33
+ MIN_PRECISION_DEFAULT = 20
34
+ PRECISION_DEFAULT = 1000
35
+ ALPHA_DEFAULT = 0.05
36
+
37
+ # Constants used for PDF generation.
38
+ #
39
+ PDF_HEIGHT_WIDTH_DEFAULT = 7
40
+
41
+ # Constants used to define benchmarking
42
+ #
43
+ COVERAGE_DEFAULT = 0.05
44
+
45
+
46
+ program :name, 'ppbench'
47
+ program :version, "#{Ppbench::VERSION}"
48
+ program :description, 'Ping pong benchmark'
49
+ program :help, 'Author', 'Nane Kratzke <nane.kratzke@fh-luebeck.de>'
50
+
51
+ global_option '--precision POINTS', Integer, PRECISION_DESCRIPTION
52
+ global_option '--naming FILE', String, NAMING_DESCRIPTION
53
+ global_option '--alpha FLOAT', Float, ALPHA_DESCRIPTION
54
+
55
+ default_command :help
56
+
57
+ # Validates and processes global options like
58
+ # - precision (used for comparison lines, median lines and confidence band plotting)
59
+ # - naming (used for user defined naming of machine and experiment tags)
60
+ #
61
+
62
+ def validate_global_options(args, options)
63
+ options.default :precision => PRECISION_DEFAULT
64
+ options.default :naming => ''
65
+ options.default :alpha => ALPHA_DEFAULT
66
+
67
+ if options.precision < MIN_PRECISION_DEFAULT
68
+ $stderr.puts("Error in --precision flag: Precision must be >= 20 points.\n")
69
+ exit!
70
+ end
71
+
72
+ if options.precision < 0
73
+ $stderr.puts("Error in --precision flag: Precision must be >= 1 point.\n")
74
+ exit!
75
+ end
76
+
77
+ Ppbench::precision = options.precision
78
+
79
+ if !options.naming.empty? && !File.exist?(options.naming)
80
+ $stderr.puts("Error in --naming flag: File '#{options.naming}' does not exist.")
81
+ exit!
82
+ end
83
+
84
+ Ppbench::naming = {} if options.naming.empty?
85
+
86
+ unless options.naming.empty?
87
+ begin
88
+ file = File.read(options.naming)
89
+ Ppbench::naming = JSON.parse(file)
90
+ rescue Exception => ex
91
+ $stderr.puts("Error in naming file '#{options.naming}'. Does not seem to be a valid JSON file.")
92
+ exit!
93
+ end
94
+ end
95
+
96
+ if options.alpha < 0.0 || options.alpha > 1.0
97
+ $stderr.puts("Error in --alpha flag: Alpha must be between 0.0 and 1.0, but alpha was '#{options.alpha}'.")
98
+ exit!
99
+ end
100
+
101
+ Ppbench::alpha = options.alpha
102
+ end
103
+
104
+ # Validates command line flags of the run command.
105
+ #
106
+ def validate_run_options(args, options)
107
+
108
+ if (options.machine.empty?)
109
+ $stderr.puts("You have to tag your benchmark data with the --machine flag.\n")
110
+ exit!
111
+ end
112
+
113
+ if (options.experiment.empty?)
114
+ $stderr.puts("You have to tag your benchmark data with the --experiment flag.\n")
115
+ exit!
116
+ end
117
+
118
+ if options.coverage < 0 || options.coverage > 1.0
119
+ $stderr.puts("Error in --coverage flag: Coverage must be in [0..1.0]\n")
120
+ exit!
121
+ end
122
+
123
+ if options.repetitions < 1
124
+ $stderr.puts("Error in --repetitions flag: Repetitions must be >= 1\n")
125
+ exit!
126
+ end
127
+
128
+ if options.concurrency < 1
129
+ $stderr.puts("Error in --concurrency flag: Concurrency must be >= 1\n")
130
+ exit!
131
+ end
132
+
133
+ if options.timeout < 1
134
+ $stderr.puts("Error in --timeout flag: Timeout must be >= 1 seconds\n")
135
+ exit!
136
+ end
137
+
138
+ if args.empty?
139
+ $stderr.puts("You have to specify a log file.\n")
140
+ exit!
141
+ end
142
+
143
+ if args.length > 1
144
+ $stderr.puts("You should only specify one log file. You specified #{args.length} logfiles.\n")
145
+ exit!
146
+ end
147
+
148
+ if File.exist?(args[0])
149
+ $stderr.puts("Logfile #{args[0]} already exists. You do not want to overwrite collected benchmark data.\n")
150
+ exit!
151
+ end
152
+
153
+ end
154
+
155
+ # Validates command line flags of the xyz-comparison-plot commands.
156
+ #
157
+ def validate_comparison_options(args, options)
158
+
159
+ if args.empty?
160
+ $stderr.puts("You have to provide benchmark files (*.csv) to analyze.")
161
+ exit!
162
+ end
163
+
164
+ if options.recwindow < 0
165
+ $stderr.puts("Error in --recwindow flag: TCP standard receive window must be >= 0 bytes.\n")
166
+ exit!
167
+ end
168
+
169
+ if options.yaxis_max < 0
170
+ $stderr.puts("Error in --yaxis_max flag: Maximum value on yaxis must be >= 0.\n")
171
+ end
172
+
173
+ if options.xaxis_max < 0
174
+ $stderr.puts("Error in --xaxis_max flag: Maximum value on xaxis must be >= 0.\n")
175
+ exit!
176
+ end
177
+
178
+ if options.xaxis_steps <= 0
179
+ $stderr.puts("Error in --xaxis_steps flag: You must provide a positive step > 0.\n")
180
+ exit!
181
+ end
182
+
183
+ end
184
+
185
+ # Validates command line flags of the xyz-plot commands.
186
+ #
187
+ def validate_plot_options(args, options)
188
+
189
+ if args.empty?
190
+ $stderr.puts("You have to provide benchmark files (*.csv) to analyze.")
191
+ exit!
192
+ end
193
+
194
+ if options.recwindow < 0
195
+ $stderr.puts("Error in --recwindow flag: TCP standard receive window must be >= 0 bytes\n")
196
+ exit!
197
+ end
198
+
199
+ if options.confidence < 0 || options.confidence > 100
200
+ $stderr.puts("Error in --confidence flag: Confidence interval must be between 0 and 100 %.\n")
201
+ exit!
202
+ end
203
+
204
+ if options.yaxis_max < 0
205
+ $stderr.puts("Error in --yaxis_max flag: Maximum value on yaxis must be >= 0.\n")
206
+ end
207
+
208
+ if options.yaxis_steps <= 0
209
+ $stderr.puts("Error in --yaxis_steps flag: You must provide a positive step > 0.\n")
210
+ exit!
211
+ end
212
+
213
+ if options.xaxis_max < 0
214
+ $stderr.puts("Error in --xaxis_max flag: Maximum value on xaxis must be >= 0.\n")
215
+ exit!
216
+ end
217
+
218
+ if options.xaxis_steps <= 0
219
+ $stderr.puts("Error in --xaxis_steps flag: You must provide a positive step > 0.\n")
220
+ exit!
221
+ end
222
+
223
+ if options.nopoints && !options.withbands
224
+ $stderr.puts("Error in --nopoints flag. You must use --withbands if applying --nopoints. Otherwise nothing would be plotted.\n")
225
+ exit!
226
+ end
227
+
228
+ end
229
+
230
+ # Validates command line options dealing with PDF generation.
231
+ #
232
+ def validate_pdf_options(args, options)
233
+ if options.height < 1
234
+ $stderr.puts("Error in --height flag. You must provide a positive height >= 1 in inch.\n")
235
+ exit!
236
+ end
237
+
238
+ if options.width < 1
239
+ $stderr.puts("Error in --width flag. You must provide a positive width >= 1 in inch.\n")
240
+ exit!
241
+ end
242
+ end
243
+
244
+ # Validates command line options for the naming-template command.
245
+ #
246
+ def validate_naming_options(args, options)
247
+ if args.empty?
248
+ $stderr.puts("Error due to missing files to analyze. You must provide at least one log file (csv).\n")
249
+ exit!
250
+ end
251
+
252
+ if !options.update.empty? && !File.exist?(options.update)
253
+ $stderr.puts("Error: File #{options.update} does not exist.\n")
254
+ exit!
255
+ end
256
+ end
257
+
258
+ # Implements the run command.
259
+ #
260
+ def run(args, options)
261
+
262
+ logfile = args[0]
263
+
264
+ Ppbench::run_bench(
265
+ options.host,
266
+ logfile,
267
+ machine_tag: options.machine,
268
+ experiment_tag: options.experiment,
269
+ coverage: options.coverage,
270
+ min: options.min,
271
+ max: options.max,
272
+ concurrency: options.concurrency,
273
+ repetitions: options.repetitions,
274
+ timeout: options.timeout
275
+ )
276
+ end
277
+
278
+ # Embeds a R script (content) into some PDF generating
279
+ # R statements.
280
+ #
281
+ def pdfout(content, file: 'output.pdf', width: 7, height: 7)
282
+ """
283
+ pdf('#{file}', width=#{width}, height=#{height})
284
+ #{content}
285
+ dev.off()
286
+ """
287
+ end
288
+
289
+ # Implements the transfer-plot command.
290
+ #
291
+ def transfer_plot(args, options)
292
+
293
+ experiments = options.experiments.split(',')
294
+ machines = options.machines.split(',')
295
+
296
+ data = Ppbench::load_data(args)
297
+ filtered_data = Ppbench::filter(
298
+ data,
299
+ experiments: experiments,
300
+ machines: machines
301
+ )
302
+ aggregated_data = Ppbench::aggregate(filtered_data)
303
+
304
+ max_x = Ppbench::maximum(aggregated_data, of: :length)
305
+ max_y = Ppbench::maximum(aggregated_data, of: :transfer_rate)
306
+
307
+ rplot = Ppbench::plotter(
308
+ aggregated_data,
309
+ to_plot: :transfer_rate,
310
+ machines: machines,
311
+ experiments: experiments,
312
+ receive_window: options.recwindow,
313
+ xaxis_max: options.xaxis_max == 0 ? max_x : options.xaxis_max,
314
+ confidence: options.confidence,
315
+ no_points: options.nopoints,
316
+ with_bands: options.withbands,
317
+ yaxis_max: options.yaxis_max == 0 ? max_y : options.yaxis_max,
318
+ yaxis_steps: options.yaxis_steps,
319
+ xaxis_steps: options.xaxis_steps,
320
+ title: "Data Transfer Rates",
321
+ subtitle: "bigger is better",
322
+ xaxis_title: "Message Size",
323
+ xaxis_unit: "kB",
324
+ yaxis_title: "Transfer Rate",
325
+ yaxis_unit: "MB/sec"
326
+ )
327
+
328
+ pdf = pdfout(rplot, file: options.pdf, width: options.width, height: options.height)
329
+ print("#{pdf}") unless options.pdf.empty?
330
+ print("#{rplot}") if options.pdf.empty?
331
+ end
332
+
333
+ # Implements the transfer-comparison-plot command.
334
+ #
335
+ def transfer_comparison_plot(args, options)
336
+
337
+ experiments = options.experiments.split(',')
338
+ machines = options.machines.split(',')
339
+
340
+ data = Ppbench::load_data(args)
341
+ filtered_data = Ppbench::filter(
342
+ data,
343
+ experiments: experiments,
344
+ machines: machines
345
+ )
346
+ aggregated_data = Ppbench::aggregate(filtered_data)
347
+
348
+ max_x = Ppbench::maximum(aggregated_data, of: :length)
349
+
350
+ rplot = Ppbench::comparison_plotter(
351
+ aggregated_data,
352
+ yaxis_max: options.yaxis_max,
353
+ to_plot: :transfer_rate,
354
+ machines: machines,
355
+ experiments: experiments,
356
+ receive_window: options.recwindow,
357
+ xaxis_max: options.xaxis_max == 0 ? max_x : options.xaxis_max,
358
+ xaxis_steps: options.xaxis_steps,
359
+ title: "Data transfer in relative comparison",
360
+ subtitle: "bigger is better",
361
+ xaxis_title: "Message Size",
362
+ xaxis_unit: "kB",
363
+ xaxis_divisor: 1000,
364
+ yaxis_title: "Ratio"
365
+ )
366
+
367
+ pdf = pdfout(rplot, file: options.pdf, width: options.width, height: options.height)
368
+ print("#{pdf}") unless options.pdf.empty?
369
+ print("#{rplot}") if options.pdf.empty?
370
+
371
+ end
372
+
373
+ # Implements the request-plot command.
374
+ #
375
+ def request_plot(args, options)
376
+
377
+ experiments = options.experiments.split(',')
378
+ machines = options.machines.split(',')
379
+
380
+ data = Ppbench::load_data(args)
381
+ filtered_data = Ppbench::filter(
382
+ data,
383
+ experiments: experiments,
384
+ machines: machines
385
+ )
386
+ aggregated_data = Ppbench::aggregate(filtered_data)
387
+
388
+ max_x = Ppbench::maximum(aggregated_data, of: :length)
389
+ max_y = Ppbench::maximum(aggregated_data, of: :rps)
390
+
391
+ rplot = Ppbench::plotter(
392
+ aggregated_data,
393
+ to_plot: :rps,
394
+ machines: machines,
395
+ experiments: experiments,
396
+ receive_window: options.recwindow,
397
+ xaxis_max: options.xaxis_max == 0 ? max_x : options.xaxis_max,
398
+ confidence: options.confidence,
399
+ no_points: options.nopoints,
400
+ with_bands: options.withbands,
401
+ yaxis_max: options.yaxis_max == 0 ? max_y : options.yaxis_max,
402
+ yaxis_steps: options.yaxis_steps,
403
+ xaxis_steps: options.xaxis_steps,
404
+ title: "Requests per seconds",
405
+ subtitle: "bigger is better",
406
+ xaxis_title: "Message Size",
407
+ xaxis_unit: "kB",
408
+ yaxis_title: "Requests per seconds",
409
+ yaxis_unit: "Req/sec",
410
+ yaxis_divisor: 1
411
+ )
412
+
413
+ pdf = pdfout(rplot, file: options.pdf, width: options.width, height: options.height)
414
+ print("#{pdf}") unless options.pdf.empty?
415
+ print("#{rplot}") if options.pdf.empty?
416
+ end
417
+
418
+ # Implements the request-comparison-plot command
419
+ #
420
+ def request_comparison_plot(args, options)
421
+
422
+ experiments = options.experiments.split(',')
423
+ machines = options.machines.split(',')
424
+
425
+ data = Ppbench::load_data(args)
426
+ filtered_data = Ppbench::filter(
427
+ data,
428
+ experiments: experiments,
429
+ machines: machines
430
+ )
431
+ aggregated_data = Ppbench::aggregate(filtered_data)
432
+
433
+ max_x = Ppbench::maximum(aggregated_data, of: :length)
434
+
435
+ rplot = Ppbench::comparison_plotter(
436
+ aggregated_data,
437
+ yaxis_max: options.yaxis_max,
438
+ to_plot: :rps,
439
+ machines: machines,
440
+ experiments: experiments,
441
+ receive_window: options.recwindow,
442
+ xaxis_max: options.xaxis_max == 0 ? max_x : options.xaxis_max,
443
+ xaxis_steps: options.xaxis_steps,
444
+ title: "Requests per second in relative comparison",
445
+ subtitle: "bigger is better",
446
+ xaxis_title: "Message Size",
447
+ xaxis_unit: "kB",
448
+ xaxis_divisor: 1000,
449
+ yaxis_title: "Ratio",
450
+ )
451
+
452
+ pdf = pdfout(rplot, file: options.pdf, width: options.width, height: options.height)
453
+ print("#{pdf}") unless options.pdf.empty?
454
+ print("#{rplot}") if options.pdf.empty?
455
+ end
456
+
457
+ # Implements the latency-plot command.
458
+ #
459
+ def latency_plot(args, options)
460
+
461
+ experiments = options.experiments.split(',')
462
+ machines = options.machines.split(',')
463
+
464
+ data = Ppbench::load_data(args)
465
+ filtered_data = Ppbench::filter(
466
+ data,
467
+ experiments: experiments,
468
+ machines: machines
469
+ )
470
+ aggregated_data = Ppbench::aggregate(filtered_data)
471
+
472
+ max_y = Ppbench::maximum(aggregated_data, of: :tpr)
473
+ max_x = Ppbench::maximum(aggregated_data, of: :length)
474
+
475
+ rplot = Ppbench::plotter(
476
+ aggregated_data,
477
+ to_plot: :tpr,
478
+ machines: machines,
479
+ experiments: experiments,
480
+ receive_window: options.recwindow,
481
+ xaxis_max: options.xaxis_max == 0 ? max_x : options.xaxis_max,
482
+ confidence: options.confidence,
483
+ no_points: options.nopoints,
484
+ with_bands: options.withbands,
485
+ yaxis_max: options.yaxis_max == 0 ? max_y : options.yaxis_max,
486
+ yaxis_steps: options.yaxis_steps,
487
+ xaxis_steps: options.xaxis_steps,
488
+ title: "Round-trip latency",
489
+ subtitle: "smaller is better",
490
+ xaxis_title: "Message Size",
491
+ xaxis_unit: "kB",
492
+ yaxis_title: "Latency",
493
+ yaxis_unit: "ms",
494
+ yaxis_divisor: 1
495
+ )
496
+
497
+ pdf = pdfout(rplot, file: options.pdf, width: options.width, height: options.height)
498
+ print("#{pdf}") unless options.pdf.empty?
499
+ print("#{rplot}") if options.pdf.empty?
500
+ end
501
+
502
+ # Implements the latency-comparison-plot command
503
+ #
504
+ def latency_comparison_plot(args, options)
505
+
506
+ experiments = options.experiments.split(',')
507
+ machines = options.machines.split(',')
508
+
509
+ data = Ppbench::load_data(args)
510
+ filtered_data = Ppbench::filter(
511
+ data,
512
+ experiments: experiments,
513
+ machines: machines
514
+ )
515
+ aggregated_data = Ppbench::aggregate(filtered_data)
516
+
517
+ max_x = Ppbench::maximum(aggregated_data, of: :length)
518
+
519
+ rplot = Ppbench::comparison_plotter(
520
+ aggregated_data,
521
+ yaxis_max: options.yaxis_max,
522
+ to_plot: :tpr,
523
+ machines: machines,
524
+ experiments: experiments,
525
+ receive_window: options.recwindow,
526
+ xaxis_max: options.xaxis_max == 0 ? max_x : options.xaxis_max,
527
+ xaxis_steps: options.xaxis_steps,
528
+ title: "Round-trip latency in relative comparison",
529
+ subtitle: "smaller is better",
530
+ xaxis_title: "Message Size",
531
+ xaxis_unit: "kB",
532
+ xaxis_divisor: 1000,
533
+ yaxis_title: "Ratio",
534
+ )
535
+
536
+ pdf = pdfout(rplot, file: options.pdf, width: options.width, height: options.height)
537
+ print("#{pdf}") unless options.pdf.empty?
538
+ print("#{rplot}") if options.pdf.empty?
539
+ end
540
+
541
+ # Implements the summary command.
542
+ #
543
+ def summary(args, options)
544
+
545
+ experiments = options.experiments.split(',')
546
+ machines = options.machines.split(',')
547
+
548
+ data = Ppbench::load_data(args)
549
+ filtered_data = Ppbench::filter(
550
+ data,
551
+ experiments: experiments,
552
+ machines: machines
553
+ )
554
+ aggregated_data = Ppbench::aggregate(filtered_data)
555
+
556
+ rows = []
557
+ aggregated_data.each do |experiment, machines|
558
+ machines.each do |machine, data|
559
+ mtr = data.map { |e| e[:transfer_rate] }.median / 1000 # median transfer rate
560
+ tpr = data.map { |e| e[:tpr] }.median # median round trip latency
561
+ rps = 1000 / tpr # median request per second
562
+
563
+ rows << [experiment, machine, data.count, "%.2f" % mtr, "%.2f" % rps, "%.2f" % tpr]
564
+ end
565
+ rows << :separator
566
+ end
567
+ rows.pop
568
+
569
+ print("We have data for: \n")
570
+ table = Terminal::Table.new(:headings => ['Experiment', 'Machine', 'Samples', 'Transfer (kB/s)', "Requests/sec", "Latency (ms)"], :rows => rows)
571
+ table.align_column(2, :right)
572
+ table.align_column(3, :right)
573
+ table.align_column(4, :right)
574
+ table.align_column(5, :right)
575
+ print("#{table}\n")
576
+ end
577
+
578
+ # Implements the naming command.
579
+ #
580
+ def naming(args, options)
581
+ experiments = options.experiments.split(',')
582
+ machines = options.machines.split(',')
583
+
584
+ data = Ppbench::load_data(args)
585
+ filtered_data = Ppbench::filter(
586
+ data,
587
+ experiments: experiments,
588
+ machines: machines
589
+ )
590
+
591
+ json_to_update = {}
592
+ existing_machine_names = []
593
+ existing_experiment_names = []
594
+ unless options.update.empty?
595
+ begin
596
+ file = File.read(options.update)
597
+ json_to_update = JSON.parse(file)
598
+ rescue Exception => ex
599
+ $stderr.puts("Could not process #{options.update}. It does not seem to be a valid JSON file.")
600
+ exit!
601
+ end
602
+ end
603
+
604
+ existing_machine_names = json_to_update['machines'].keys if json_to_update['machines']
605
+ existing_experiment_names = json_to_update['experiments'].keys if json_to_update['experiments']
606
+
607
+ machine_names = filtered_data.map { |entry| entry[:machine] }.uniq.sort - existing_machine_names
608
+ experiment_names = filtered_data.map { |entry| entry[:experiment] }.uniq.sort - existing_experiment_names
609
+
610
+ machines_map = (
611
+ machine_names.map { |e| [e, "#{e} (TODO: Provide a better name)"] } +
612
+ existing_machine_names.map { |e| [e, json_to_update['machines'][e]] }
613
+ ).to_h
614
+
615
+ experiments_map = (
616
+ experiment_names.map { |e| [e, "#{e} (TODO: Provide a better name)"] } +
617
+ existing_experiment_names.map { |e| [e, json_to_update['experiments'][e]] }
618
+ ).to_h
619
+
620
+ json = { "machines": machines_map, "experiments": experiments_map }
621
+ print ("#{JSON.pretty_generate(json)}")
622
+ end
623
+
624
+ # Implements the citation command.
625
+ #
626
+ def citation(args, options)
627
+ bibtex =
628
+ """
629
+ @misc{Kra2015,
630
+ title = {A distributed HTTP-based and REST-like ping-pong system for test and benchmarking purposes.},
631
+ author = {{Nane Kratzke}},
632
+ organization = {L\\\"ubeck University of Applied Sciences},
633
+ address = {L\\\"ubeck, Germany},
634
+ year = {2015},
635
+ howpublished = {\\url{https://github.com/nkratzke/pingpong}}
636
+ }
637
+ """
638
+
639
+ return bibtex if options.bibtex
640
+
641
+ """
642
+ To cite ppbench in publications use:
643
+
644
+ Kratzke, Nane (2015). A distributed HTTP-based and REST-like ping-pong system for test and benchmarking purposes.
645
+ Lübeck University of Applied Sciences, Lübeck, Germany. URL https://github.com/nkratzke/pingpong.
646
+
647
+ A BibTeX entry for LaTeX users is: #{bibtex}
648
+
649
+ """
650
+ end
651
+
652
+ command :run do |c|
653
+ c.syntax = 'ppbench run [options] log.csv'
654
+ c.description = 'Runs a ping pong benchmark.'
655
+ c.example 'Run a benchmark and tags the results as to be collected on a m3.2xlarge instance running a docker experiment',
656
+ 'ppbench run --host http://1.2.3.4:8080 --machine m3.2xlarge --experiment docker log.csv'
657
+
658
+ c.option '--host STRING', String, 'Host'
659
+ c.option '--machine STRING', String, 'A tag to categorize the machine (defaults to empty String)'
660
+ c.option '--experiment STRING', String, 'A tag to categorize the experiment (defaults to empty String)'
661
+ c.option '--min INT', Integer, 'Minimum message size [bytes] (defaults to 1)'
662
+ c.option '--max INT', Integer, 'Maximum message size [bytes] (defaults to 500.000)'
663
+ c.option '--coverage FLOAT', Float, 'Amount of requests to send (defaults to 5% == 0.05, must be between 0.0 and 1.0)'
664
+ c.option '--repetitions INT', Integer, 'Repetitions for each data point to collect (defaults to 1, must be >= 1)'
665
+ c.option '--concurrency INT', Integer, 'Requests to be send at the same time in parallel (defaults to 1, must be >= 1)'
666
+ c.option '--timeout SECONDS', Integer, 'Timeout in seconds (defaults to 60 seconds, must be >= 1)'
667
+
668
+ c.action do |args, options|
669
+
670
+ options.default :min => 1, :max => 500000
671
+ options.default :machine => ''
672
+ options.default :experiment => ''
673
+ options.default :coverage => COVERAGE_DEFAULT
674
+ options.default :repetitions => 1
675
+ options.default :concurrency => 1
676
+ options.default :timeout => 60
677
+
678
+ validate_global_options(args, options)
679
+ validate_run_options(args, options)
680
+ run(args, options)
681
+ print("Finished\n")
682
+ end
683
+ end
684
+
685
+ command 'transfer-plot' do |c|
686
+ c.syntax = 'ppbench transfer-plot [options] *.csv'
687
+ c.summary = 'Plots data transfer rates in an absolute way.'
688
+ c.description = 'Generates a R script to plot data transfer rates in an absolute way.'
689
+
690
+ c.option '--machines LIST', String, MACHINES_DESCRIPTION
691
+ c.option '--experiments LIST', String, EXPERIMENTS_DESCRIPTION
692
+ c.option '--recwindow BYTES', Integer, RECWINDOW_DESCRIPTION
693
+
694
+ c.option '--yaxis_max FLOAT', Float, YAXIS_MAX_DESCRIPTION
695
+ c.option '--yaxis_steps TICKS', Integer, YAXIS_STEPS_DESCRIPTION
696
+ c.option '--xaxis_max BYTES', Integer, YAXIS_MAX_DESCRIPTION
697
+ c.option '--xaxis_steps TICKS', Integer, XAXIS_STEPS_DESCRIPTION
698
+
699
+ c.option '--confidence PERCENT' , Integer, CONFIDENCE_DESCRIPTION
700
+ c.option '--withbands', WITHBANDS_DESCRIPTION
701
+ c.option '--nopoints', NOPOINTS_DESCRIPTION
702
+
703
+ c.option '--pdf FILE', String, PDF_DESCRIPTION
704
+ c.option '--width INCH', Integer, PDF_WIDTH_DESCRIPTION
705
+ c.option '--height INCH', Integer, PDF_HEIGHT_DESCRIPTION
706
+
707
+ c.action do |args, options|
708
+ options.default :machines => ''
709
+ options.default :experiments => ''
710
+ options.default :recwindow => RECWINDOW_DEFAULT
711
+ options.default :confidence => CONFIDENCE_DEFAULT
712
+
713
+ options.default :yaxis_max => 0
714
+ options.default :yaxis_steps => AXIS_STEP_DEFAULT
715
+ options.default :xaxis_max => 0
716
+ options.default :xaxis_steps => AXIS_STEP_DEFAULT
717
+
718
+ options.default :pdf => ''
719
+ options.default :width => PDF_HEIGHT_WIDTH_DEFAULT
720
+ options.default :height => PDF_HEIGHT_WIDTH_DEFAULT
721
+
722
+ validate_global_options(args, options)
723
+ validate_plot_options(args, options)
724
+ validate_pdf_options(args,options)
725
+ transfer_plot(args, options)
726
+ end
727
+ end
728
+
729
+
730
+ command 'transfer-comparison-plot' do |c|
731
+ c.syntax = 'ppbench transfer-comparison-plot [options] *.csv'
732
+ c.summary = 'Compares data transfer rates in a relative way.'
733
+ c.description = 'Generates a R script to compare data transfer rates in a relative way.'
734
+
735
+ c.option '--machines LIST', String, MACHINES_DESCRIPTION
736
+ c.option '--experiments LIST', String, EXPERIMENTS_DESCRIPTION
737
+ c.option '--recwindow BYTES', Integer, RECWINDOW_DESCRIPTION
738
+
739
+ c.option '--yaxis_max BYTES', Float, YAXIS_MAX_DESCRIPTION
740
+ c.option '--yaxis_steps STEPS', Integer, YAXIS_STEPS_DESCRIPTION
741
+ c.option '--xaxis_max BYTES', Integer, XAXIS_MAX_DESCRIPTION
742
+ c.option '--xaxis_steps STEPS', Integer, XAXIS_STEPS_DESCRIPTION
743
+
744
+ c.option '--pdf FILE', String, PDF_DESCRIPTION
745
+ c.option '--width INTEGER', Integer, PDF_WIDTH_DESCRIPTION
746
+ c.option '--height INTEGER', Integer, PDF_HEIGHT_DESCRIPTION
747
+
748
+ c.action do |args, options|
749
+
750
+ options.default :machines => ''
751
+ options.default :experiments => ''
752
+ options.default :recwindow => RECWINDOW_DEFAULT
753
+
754
+ options.default :yaxis_max => COMPARISON_MAX_DEFAULT
755
+ options.default :yaxis_steps => AXIS_STEP_DEFAULT
756
+ options.default :xaxis_max => 0
757
+ options.default :xaxis_steps => AXIS_STEP_DEFAULT
758
+
759
+ options.default :pdf => ''
760
+ options.default :width => PDF_HEIGHT_WIDTH_DEFAULT
761
+ options.default :height => PDF_HEIGHT_WIDTH_DEFAULT
762
+
763
+ validate_global_options(args, options)
764
+ validate_comparison_options(args, options)
765
+ validate_pdf_options(args,options)
766
+ transfer_comparison_plot(args, options)
767
+ end
768
+ end
769
+
770
+ command 'request-plot' do |c|
771
+ c.syntax = 'ppbench request-plot [options] *.csv'
772
+ c.summary = 'Plots requests per second in an absolute way.'
773
+ c.description = 'Generates a R script to plot requests per second in an absolute way.'
774
+
775
+ c.option '--machines LIST', String, MACHINES_DESCRIPTION
776
+ c.option '--experiments LIST', String, EXPERIMENTS_DESCRIPTION
777
+ c.option '--recwindow BYTES', Integer, RECWINDOW_DESCRIPTION
778
+
779
+ c.option '--yaxis_max REQS', Float, YAXIS_MAX_DESCRIPTION
780
+ c.option '--yaxis_steps STEPS', Integer, YAXIS_STEPS_DESCRIPTION
781
+ c.option '--xaxis_max BYTES', Integer, XAXIS_MAX_DESCRIPTION
782
+ c.option '--xsteps STEPS', Integer, YAXIS_STEPS_DESCRIPTION
783
+
784
+ c.option '--confidence PERCENT' , Integer, CONFIDENCE_DESCRIPTION
785
+ c.option '--withbands', WITHBANDS_DESCRIPTION
786
+ c.option '--nopoints', NOPOINTS_DESCRIPTION
787
+
788
+ c.option '--pdf FILE', String, PDF_DESCRIPTION
789
+ c.option '--width INCH', Integer, PDF_WIDTH_DESCRIPTION
790
+ c.option '--height INCH', Integer, PDF_HEIGHT_DESCRIPTION
791
+
792
+ c.action do |args, options|
793
+ options.default :machines => ''
794
+ options.default :experiments => ''
795
+ options.default :recwindow => RECWINDOW_DEFAULT
796
+ options.default :confidence => CONFIDENCE_DEFAULT
797
+
798
+ options.default :yaxis_max => 0
799
+ options.default :yaxis_steps => AXIS_STEP_DEFAULT
800
+ options.default :xaxis_max => 0
801
+ options.default :xaxis_steps => AXIS_STEP_DEFAULT
802
+
803
+ options.default :pdf => ''
804
+ options.default :width => PDF_HEIGHT_WIDTH_DEFAULT
805
+ options.default :height => PDF_HEIGHT_WIDTH_DEFAULT
806
+
807
+ validate_global_options(args, options)
808
+ validate_plot_options(args, options)
809
+ validate_pdf_options(args, options)
810
+ request_plot(args, options)
811
+ end
812
+ end
813
+
814
+ command 'request-comparison-plot' do |c|
815
+ c.syntax = 'ppbench request-comparison-plot [options] *.csv'
816
+ c.summary = 'Compares requests per second in a relative way.'
817
+ c.description = 'Generates a R script to compare requests per second in a relative way.'
818
+
819
+ c.option '--machines STRING', String, MACHINES_DESCRIPTION
820
+ c.option '--experiments STRING', String, EXPERIMENTS_DESCRIPTION
821
+ c.option '--recwindow INTEGER', Integer, RECWINDOW_DESCRIPTION
822
+
823
+ c.option '--yaxis_max FLOAT', Float, YAXIS_MAX_DESCRIPTION
824
+
825
+ c.option '--xaxis_max BYTES', Integer, XAXIS_MAX_DESCRIPTION
826
+ c.option '--xaxis_steps INTEGER', Integer, YAXIS_STEPS_DESCRIPTION
827
+
828
+ c.option '--pdf FILE', String, 'Saves output to a PDF file'
829
+ c.option '--width INTEGER', Integer, 'Width of plot in inch (defaults to 7 inch, only useful with PDF output)'
830
+ c.option '--height INTEGER', Integer, 'Height of plot in inch (defaults to 7 inch, only useful with PDF output)'
831
+
832
+ c.action do |args, options|
833
+
834
+ options.default :machines => ''
835
+ options.default :experiments => ''
836
+ options.default :recwindow => RECWINDOW_DEFAULT
837
+
838
+ options.default :yaxis_max => COMPARISON_MAX_DEFAULT
839
+ options.default :xaxis_max => 0
840
+ options.default :xaxis_steps => AXIS_STEP_DEFAULT
841
+
842
+ options.default :pdf => ''
843
+ options.default :width => PDF_HEIGHT_WIDTH_DEFAULT
844
+ options.default :height => PDF_HEIGHT_WIDTH_DEFAULT
845
+
846
+ validate_global_options(args, options)
847
+ validate_comparison_options(args, options)
848
+ validate_pdf_options(args, options)
849
+ request_comparison_plot(args, options)
850
+ end
851
+ end
852
+
853
+ command 'latency-plot' do |c|
854
+ c.syntax = 'ppbench latency-plot [options] *.csv'
855
+ c.summary = 'Plots round-trip latencies in an absolute way.'
856
+ c.description = 'Generates a R script to plot round-trip latencies in an absolute way.'
857
+
858
+ c.example 'Generates a latency plot for data collected on machine m3.xlarge for java, dart and go implementations of the ping-pong system.',
859
+ 'ppbench latency-plot --machines m3.xlarge --experiments bare-java,bare-go,bare-dart *.csv > bare-comparison.R'
860
+
861
+ c.example 'Generates a latency plot for data collected on machine m3.xlarge for java, dart implementations of the ping-pong system.',
862
+ 'ppbench latency-comparison-plot --machines m3.xlarge --experiments bare-java,bare-go --pdf compare.pdf --width 9, --height 6 *.csv > bare-comparison.R'
863
+
864
+ c.option '--machines LIST', String, MACHINES_DESCRIPTION
865
+ c.option '--experiments LIST', String, EXPERIMENTS_DESCRIPTION
866
+ c.option '--recwindow BYTES', Integer, RECWINDOW_DESCRIPTION
867
+
868
+ c.option '--yaxis_max MS', Integer, YAXIS_MAX_DESCRIPTION
869
+ c.option '--yaxis_steps TICKS', Integer, YAXIS_STEPS_DESCRIPTION
870
+ c.option '--xaxis_max BYTES', Integer, XAXIS_MAX_DESCRIPTION
871
+ c.option '--xaxis_steps TICKS', Integer, XAXIS_STEPS_DESCRIPTION
872
+
873
+ c.option '--confidence PERCENT' , Integer, CONFIDENCE_DESCRIPTION
874
+ c.option '--withbands', WITHBANDS_DESCRIPTION
875
+ c.option '--nopoints', NOPOINTS_DESCRIPTION
876
+
877
+ c.option '--pdf FILE', String, PDF_DESCRIPTION
878
+ c.option '--width INCH', Integer, PDF_WIDTH_DESCRIPTION
879
+ c.option '--height INCH', Integer, PDF_HEIGHT_DESCRIPTION
880
+
881
+ c.action do |args, options|
882
+
883
+ options.default :machines => ''
884
+ options.default :experiments => ''
885
+ options.default :recwindow => RECWINDOW_DEFAULT
886
+
887
+ options.default :confidence => CONFIDENCE_DEFAULT
888
+
889
+ options.default :yaxis_max => 0
890
+ options.default :yaxis_steps => AXIS_STEP_DEFAULT
891
+ options.default :xaxis_max => 0
892
+ options.default :xaxis_steps => AXIS_STEP_DEFAULT
893
+
894
+ options.default :pdf => ''
895
+ options.default :width => PDF_HEIGHT_WIDTH_DEFAULT
896
+ options.default :height => PDF_HEIGHT_WIDTH_DEFAULT
897
+
898
+ validate_global_options(args, options)
899
+ validate_plot_options(args, options)
900
+ validate_pdf_options(args, options)
901
+ latency_plot(args, options)
902
+ end
903
+ end
904
+
905
+ command 'latency-comparison-plot' do |c|
906
+ c.syntax = 'ppbench latency-comparison-plot [options] *.csv'
907
+ c.summary = 'Compares round-trip latencies in a relative way.'
908
+ c.description = 'Generates a R script to compare round-trip latencies in a relative way.'
909
+
910
+ c.example 'Generates a latency comparison plot for data collected on machine m3.xlarge for java, dart and go implementations of the ping-pong system.',
911
+ 'ppbench latency-comparison-plot --machines m3.xlarge --experiments bare-java,bare-go,bare-dart *.csv > bare-comparison.R'
912
+
913
+ c.example 'Generates a latency comparison plot as PDF using Rscript',
914
+ 'ppbench latency-comparison-plot -m m3.xlarge -e bare-java,bare-go -p compare.pdf *.csv | Rscript - '
915
+
916
+ c.option '--machines LIST', String, MACHINES_DESCRIPTION
917
+ c.option '--experiments LIST', String, EXPERIMENTS_DESCRIPTION
918
+ c.option '--recwindow BYTES', Integer, RECWINDOW_DESCRIPTION
919
+
920
+ c.option '--yaxis_max MS', Float, 'Maximum Y Value (must be greater than 1.0, defaults to 2.0)'
921
+ c.option '--xaxis_max BYTES', Integer, XAXIS_MAX_DESCRIPTION
922
+ c.option '--xaxis_steps TICKS', Integer, YAXIS_STEPS_DESCRIPTION
923
+
924
+ c.option '--pdf FILE', String, PDF_DESCRIPTION
925
+ c.option '--width INCH', Integer, PDF_WIDTH_DESCRIPTION
926
+ c.option '--height INCH', Integer, PDF_HEIGHT_DESCRIPTION
927
+
928
+ c.action do |args, options|
929
+
930
+ options.default :machines => ''
931
+ options.default :experiments => ''
932
+ options.default :recwindow => RECWINDOW_DEFAULT
933
+
934
+ options.default :yaxis_max => COMPARISON_MAX_DEFAULT
935
+ options.default :xaxis_max => 0
936
+ options.default :xaxis_steps => AXIS_STEP_DEFAULT
937
+
938
+ options.default :pdf => ''
939
+ options.default :width => PDF_HEIGHT_WIDTH_DEFAULT
940
+ options.default :height => PDF_HEIGHT_WIDTH_DEFAULT
941
+
942
+ validate_global_options(args, options)
943
+ validate_comparison_options(args, options)
944
+ validate_pdf_options(args, options)
945
+ latency_comparison_plot(args, options)
946
+ end
947
+ end
948
+
949
+ command :summary do |c|
950
+ c.syntax = 'ppbench summary [options] *.csv'
951
+ c.summary = 'Summarizes benchmark data.'
952
+ c.description = 'Summarizes benchmark data. Useful to inspect data for completeness or to query machine and experiment tags.'
953
+
954
+ c.example 'Lists a summary of all benchmark data.',
955
+ 'ppbench summary *.csv'
956
+ c.example 'Lists a summary of all benchmark data tagged to be run on machines m3.2xlarge, m3.xlarge',
957
+ 'ppbench summary --machines m3.2xlarge,m3.xlarge *.csv'
958
+ c.example 'Lists a summary of all benchmark data tagged to be run as docker-java or bare-dart experiments',
959
+ 'ppbench summary --experiments bare-dart,docker-java *.csv'
960
+
961
+ c.option '--machines LIST', String, MACHINES_DESCRIPTION
962
+ c.option '--experiments LIST', String, EXPERIMENTS_DESCRIPTION
963
+
964
+ c.action do |args, options|
965
+ options.default :machines => ''
966
+ options.default :experiments => ''
967
+
968
+ summary(args, options)
969
+ end
970
+ end
971
+
972
+ command 'naming-template' do |c|
973
+
974
+ c.syntax = 'ppbench naming-template [options] *.csv'
975
+ c.summary = 'Generates a JSON file for naming.'
976
+ c.description = 'Generates a JSON file from benchmark data for naming. This file can be used with the --naming flag.'
977
+
978
+ c.example 'Generates a naming template from all benchmark data.',
979
+ 'ppbench naming-template *.csv > naming.json'
980
+ c.example 'Appends all missing names to an existing naming-template',
981
+ 'ppbench naming-template --update naming.json > naming-update.json'
982
+
983
+ c.option '--machines LIST', String, MACHINES_DESCRIPTION
984
+ c.option '--experiments LIST', String, EXPERIMENTS_DESCRIPTION
985
+ c.option '--update FILE', 'Naming file to update with additional data.'
986
+
987
+ c.action do |args, options|
988
+ options.default :machines => ''
989
+ options.default :experiments => ''
990
+ options.default :update => ''
991
+
992
+ validate_naming_options(args, options)
993
+ naming(args, options)
994
+ end
995
+
996
+ end
997
+
998
+ command :citation do |c|
999
+ c.syntax = 'ppbench citation [options]'
1000
+ c.summary = 'Citation information about ppbench.'
1001
+ c.description = 'Provides information how to cite ppbench in publications.'
1002
+
1003
+ c.example 'Get general information how to cite ppbench in publications',
1004
+ 'ppbench citation'
1005
+ c.example 'Append a bibtex entry for ppbench to your references.bib (LaTex users).',
1006
+ 'ppbench citation --bibtex >> references.bib'
1007
+ c.option '--bibtex', 'Get bibtex entry (for Latex users)'
1008
+
1009
+ c.action do |args, options|
1010
+ print "#{citation(args, options)}\n"
1011
+ end
1012
+ end