serialbench 0.1.1 → 0.1.2

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.
Files changed (84) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/benchmark.yml +13 -5
  3. data/.github/workflows/docker.yml +35 -9
  4. data/.github/workflows/rake.yml +15 -0
  5. data/Gemfile +2 -1
  6. data/README.adoc +267 -1129
  7. data/Rakefile +0 -55
  8. data/config/benchmarks/full.yml +29 -0
  9. data/config/benchmarks/short.yml +26 -0
  10. data/config/environments/asdf-ruby-3.2.yml +8 -0
  11. data/config/environments/asdf-ruby-3.3.yml +8 -0
  12. data/config/environments/docker-ruby-3.0.yml +9 -0
  13. data/config/environments/docker-ruby-3.1.yml +9 -0
  14. data/config/environments/docker-ruby-3.2.yml +9 -0
  15. data/config/environments/docker-ruby-3.3.yml +9 -0
  16. data/config/environments/docker-ruby-3.4.yml +9 -0
  17. data/docker/Dockerfile.alpine +33 -0
  18. data/docker/{Dockerfile.benchmark → Dockerfile.ubuntu} +4 -3
  19. data/docker/README.md +2 -2
  20. data/exe/serialbench +1 -1
  21. data/lib/serialbench/benchmark_runner.rb +261 -423
  22. data/lib/serialbench/cli/base_cli.rb +51 -0
  23. data/lib/serialbench/cli/benchmark_cli.rb +380 -0
  24. data/lib/serialbench/cli/environment_cli.rb +181 -0
  25. data/lib/serialbench/cli/resultset_cli.rb +215 -0
  26. data/lib/serialbench/cli/ruby_build_cli.rb +238 -0
  27. data/lib/serialbench/cli.rb +58 -601
  28. data/lib/serialbench/config_manager.rb +140 -0
  29. data/lib/serialbench/models/benchmark_config.rb +63 -0
  30. data/lib/serialbench/models/benchmark_result.rb +45 -0
  31. data/lib/serialbench/models/environment_config.rb +71 -0
  32. data/lib/serialbench/models/platform.rb +59 -0
  33. data/lib/serialbench/models/result.rb +53 -0
  34. data/lib/serialbench/models/result_set.rb +71 -0
  35. data/lib/serialbench/models/result_store.rb +108 -0
  36. data/lib/serialbench/models.rb +54 -0
  37. data/lib/serialbench/ruby_build_manager.rb +153 -0
  38. data/lib/serialbench/runners/asdf_runner.rb +296 -0
  39. data/lib/serialbench/runners/base.rb +32 -0
  40. data/lib/serialbench/runners/docker_runner.rb +142 -0
  41. data/lib/serialbench/serializers/base_serializer.rb +8 -16
  42. data/lib/serialbench/serializers/json/base_json_serializer.rb +4 -4
  43. data/lib/serialbench/serializers/json/json_serializer.rb +0 -2
  44. data/lib/serialbench/serializers/json/oj_serializer.rb +0 -2
  45. data/lib/serialbench/serializers/json/yajl_serializer.rb +0 -2
  46. data/lib/serialbench/serializers/toml/base_toml_serializer.rb +5 -3
  47. data/lib/serialbench/serializers/toml/toml_rb_serializer.rb +0 -2
  48. data/lib/serialbench/serializers/toml/tomlib_serializer.rb +0 -2
  49. data/lib/serialbench/serializers/toml/tomlrb_serializer.rb +56 -0
  50. data/lib/serialbench/serializers/xml/base_xml_serializer.rb +4 -9
  51. data/lib/serialbench/serializers/xml/libxml_serializer.rb +0 -2
  52. data/lib/serialbench/serializers/xml/nokogiri_serializer.rb +0 -2
  53. data/lib/serialbench/serializers/xml/oga_serializer.rb +0 -2
  54. data/lib/serialbench/serializers/xml/ox_serializer.rb +0 -2
  55. data/lib/serialbench/serializers/xml/rexml_serializer.rb +0 -2
  56. data/lib/serialbench/serializers/yaml/base_yaml_serializer.rb +5 -1
  57. data/lib/serialbench/serializers/yaml/syck_serializer.rb +59 -22
  58. data/lib/serialbench/serializers.rb +23 -6
  59. data/lib/serialbench/site_generator.rb +105 -0
  60. data/lib/serialbench/templates/assets/css/benchmark_report.css +535 -0
  61. data/lib/serialbench/templates/assets/css/format_based.css +526 -0
  62. data/lib/serialbench/templates/assets/css/themes.css +588 -0
  63. data/lib/serialbench/templates/assets/js/chart_helpers.js +381 -0
  64. data/lib/serialbench/templates/assets/js/dashboard.js +796 -0
  65. data/lib/serialbench/templates/assets/js/navigation.js +142 -0
  66. data/lib/serialbench/templates/base.liquid +49 -0
  67. data/lib/serialbench/templates/format_based.liquid +279 -0
  68. data/lib/serialbench/templates/partials/chart_section.liquid +4 -0
  69. data/lib/serialbench/version.rb +1 -1
  70. data/lib/serialbench.rb +2 -31
  71. data/serialbench.gemspec +4 -1
  72. metadata +86 -16
  73. data/config/ci.yml +0 -22
  74. data/config/full.yml +0 -30
  75. data/docker/run-benchmarks.sh +0 -356
  76. data/lib/serialbench/chart_generator.rb +0 -821
  77. data/lib/serialbench/result_formatter.rb +0 -182
  78. data/lib/serialbench/result_merger.rb +0 -1201
  79. data/lib/serialbench/serializers/xml/base_parser.rb +0 -69
  80. data/lib/serialbench/serializers/xml/libxml_parser.rb +0 -98
  81. data/lib/serialbench/serializers/xml/nokogiri_parser.rb +0 -111
  82. data/lib/serialbench/serializers/xml/oga_parser.rb +0 -85
  83. data/lib/serialbench/serializers/xml/ox_parser.rb +0 -64
  84. data/lib/serialbench/serializers/xml/rexml_parser.rb +0 -129
@@ -1,630 +1,87 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'thor'
4
- require 'json'
5
- require 'yaml'
6
- require 'fileutils'
4
+ require_relative 'cli/base_cli'
5
+ require_relative 'cli/environment_cli'
6
+ require_relative 'cli/benchmark_cli'
7
+ require_relative 'cli/resultset_cli'
8
+ require_relative 'cli/ruby_build_cli'
7
9
 
8
10
  module Serialbench
9
- # Thor-based command line interface for Serialbench
10
- class Cli < Thor
11
- include Thor::Actions
11
+ # Main CLI entry point for the new object-oriented command structure
12
+ class CLI < Serialbench::Cli::BaseCli
13
+ desc 'environment SUBCOMMAND', 'Manage benchmark environments'
14
+ subcommand 'environment', Serialbench::Cli::EnvironmentCli
12
15
 
13
- desc 'benchmark', 'Run serialization benchmarks'
14
- long_desc <<~DESC
15
- Run the complete benchmark suite for all available serialization libraries.
16
+ desc 'benchmark SUBCOMMAND', 'Manage individual benchmark runs'
17
+ subcommand 'benchmark', Serialbench::Cli::BenchmarkCli
16
18
 
17
- This command will test parsing, generation, streaming, and memory usage
18
- across XML, JSON, and TOML formats using all available libraries.
19
- DESC
20
- option :formats, type: :array, default: %w[xml json yaml toml],
21
- desc: 'Formats to benchmark (xml, json, yaml, toml)'
22
- option :output_format, type: :string, default: 'all',
23
- desc: 'Output format: all, json, yaml, html'
24
- option :parsing_only, type: :boolean, default: false,
25
- desc: 'Run only parsing benchmarks'
26
- option :generation_only, type: :boolean, default: false,
27
- desc: 'Run only generation benchmarks'
28
- option :streaming_only, type: :boolean, default: false,
29
- desc: 'Run only streaming benchmarks'
30
- option :memory_only, type: :boolean, default: false,
31
- desc: 'Run only memory usage benchmarks'
32
- option :iterations, type: :numeric, default: 10,
33
- desc: 'Number of benchmark iterations'
34
- option :warmup, type: :numeric, default: 3,
35
- desc: 'Number of warmup iterations'
36
- def benchmark
37
- say 'Serialbench - Comprehensive Serialization Performance Tests', :green
38
- say '=' * 70, :green
19
+ desc 'resultset SUBCOMMAND', 'Manage benchmark resultsets (collections of runs)'
20
+ subcommand 'resultset', Serialbench::Cli::ResultsetCli
39
21
 
40
- # Validate formats
41
- valid_formats = %w[xml json yaml toml]
42
- invalid_formats = options[:formats] - valid_formats
43
- unless invalid_formats.empty?
44
- say "Invalid formats: #{invalid_formats.join(', ')}", :red
45
- say "Valid formats: #{valid_formats.join(', ')}", :yellow
46
- exit 1
47
- end
48
-
49
- # Convert format strings to symbols
50
- formats = options[:formats].map(&:to_sym)
51
-
52
- # Show available serializers
53
- show_available_serializers(formats)
54
-
55
- # Run benchmarks
56
- runner_options = {
57
- formats: formats,
58
- iterations: options[:iterations],
59
- warmup: options[:warmup]
60
- }
61
-
62
- runner = Serialbench::BenchmarkRunner.new(**runner_options)
63
-
64
- begin
65
- results = run_selected_benchmarks(runner)
66
- save_results(results)
67
- show_summary(results) unless %w[json yaml].include?(options[:output_format])
68
- rescue StandardError => e
69
- say "Error running benchmarks: #{e.message}", :red
70
- say e.backtrace.first(5).join("\n"), :red if ENV['DEBUG']
71
- exit 1
72
- end
73
- end
74
-
75
- desc 'list', 'List available serializers'
76
- long_desc <<~DESC
77
- Display all available serialization libraries grouped by format.
78
-
79
- Shows which libraries are installed and available for benchmarking,
80
- along with their versions.
81
- DESC
82
- option :format, type: :string, desc: 'Show only serializers for specific format'
83
- def list
84
- say 'Available Serializers', :green
85
- say '=' * 30, :green
86
-
87
- if options[:format]
88
- format_sym = options[:format].to_sym
89
- serializers = Serialbench::Serializers.available_for_format(format_sym)
90
-
91
- if serializers.empty?
92
- say "No available serializers for format: #{options[:format]}", :yellow
93
- else
94
- show_serializers_for_format(format_sym, serializers)
95
- end
96
- else
97
- %i[xml json yaml toml].each do |format|
98
- serializers = Serialbench::Serializers.available_for_format(format)
99
- next if serializers.empty?
100
-
101
- show_serializers_for_format(format, serializers)
102
- say ''
103
- end
104
- end
105
- end
22
+ desc 'ruby_build SUBCOMMAND', 'Manage Ruby-Build definitions for validation'
23
+ subcommand 'ruby_build', Serialbench::Cli::RubyBuildCli
106
24
 
107
25
  desc 'version', 'Show Serialbench version'
108
- def version
109
- say "Serialbench version #{Serialbench::VERSION}", :green
110
- end
111
-
112
- desc 'merge_results INPUT_DIRS... OUTPUT_DIR', 'Merge benchmark results from multiple runs'
113
- long_desc <<~DESC
114
- Merge benchmark results from multiple Ruby versions or different environments.
115
-
116
- INPUT_DIRS should contain results.json files from different benchmark runs.
117
- OUTPUT_DIR will contain the merged results and comparative reports.
118
-
119
- Example:
120
- serialbench merge_results ruby-3.0/results ruby-3.1/results ruby-3.2/results merged_output/
121
- DESC
122
- def merge_results(*args)
123
- if args.length < 2
124
- say 'Error: Need at least one input directory and one output directory', :red
125
- say 'Usage: serialbench merge_results INPUT_DIRS... OUTPUT_DIR', :yellow
126
- exit 1
127
- end
128
-
129
- output_dir = args.pop
130
- input_dirs = args
131
-
132
- say "Merging benchmark results from #{input_dirs.length} directories to #{output_dir}", :green
133
-
134
- begin
135
- merger = Serialbench::ResultMerger.new
136
- merged_file = merger.merge_directories(input_dirs, output_dir)
137
- say "Results merged successfully to: #{merged_file}", :green
138
- rescue StandardError => e
139
- say "Error merging results: #{e.message}", :red
140
- exit 1
141
- end
142
- end
143
-
144
- desc 'github_pages INPUT_DIRS... OUTPUT_DIR', 'Generate GitHub Pages HTML from multiple benchmark runs'
145
- long_desc <<~DESC
146
- Merge benchmark results from multiple Ruby versions and generate a GitHub Pages compatible HTML report.
147
-
148
- INPUT_DIRS should contain results.json files from different benchmark runs.
149
- OUTPUT_DIR will contain index.html and styles.css ready for GitHub Pages deployment.
150
-
151
- This command combines merge_results and HTML generation in one step.
152
-
153
- Example:
154
- serialbench github_pages ruby-3.0/results ruby-3.1/results ruby-3.2/results docs/
155
- DESC
156
- def github_pages(*args)
157
- if args.length < 2
158
- say 'Error: Need at least one input directory and one output directory', :red
159
- say 'Usage: serialbench github_pages INPUT_DIRS... OUTPUT_DIR', :yellow
160
- exit 1
161
- end
162
-
163
- output_dir = args.pop
164
- input_dirs = args
165
-
166
- say "Generating GitHub Pages from #{input_dirs.length} benchmark directories", :green
167
-
168
- begin
169
- merger = Serialbench::ResultMerger.new
170
-
171
- # Merge results
172
- say 'Step 1: Merging benchmark results...', :yellow
173
- merger.merge_directories(input_dirs, output_dir)
174
-
175
- # Generate GitHub Pages HTML
176
- say 'Step 2: Generating GitHub Pages HTML...', :yellow
177
- files = merger.generate_github_pages_html(output_dir)
178
-
179
- say 'GitHub Pages generated successfully!', :green
180
- say 'Files created:', :cyan
181
- say " HTML: #{files[:html]}", :white
182
- say " CSS: #{files[:css]}", :white
183
- say '', :white
184
- say 'To deploy to GitHub Pages:', :cyan
185
- say '1. Commit and push the generated files to your repository', :white
186
- say '2. Enable GitHub Pages in repository settings', :white
187
- say '3. Set source to the branch containing these files', :white
188
- rescue StandardError => e
189
- say "Error generating GitHub Pages: #{e.message}", :red
190
- exit 1
191
- end
192
- end
193
-
194
- desc 'generate_reports DATA_FILE', 'Generate reports from benchmark data'
195
- long_desc <<~DESC
196
- Generate HTML and AsciiDoc reports from existing benchmark data.
197
-
198
- DATA_FILE should be a JSON file containing benchmark results.
199
- DESC
200
- def generate_reports(data_file)
201
- say "Generating reports from data in #{data_file}", :green
202
-
203
- unless File.exist?(data_file)
204
- say "Data file does not exist: #{data_file}", :red
205
- exit 1
206
- end
207
-
208
- begin
209
- Serialbench.generate_reports_from_data(data_file)
210
- say 'Reports generated successfully!', :green
211
- rescue StandardError => e
212
- say "Error generating reports: #{e.message}", :red
213
- exit 1
214
- end
215
- end
216
-
217
- desc 'analyze_performance INPUT_DIRS... OUTPUT_FILE', 'Analyze performance across multiple benchmark results'
218
- long_desc <<~DESC
219
- Analyze performance data from multiple benchmark runs and generate JSON analysis.
220
-
221
- INPUT_DIRS should contain results.json files from different benchmark runs.
222
- OUTPUT_FILE will be a JSON file with detailed performance analysis.
223
-
224
- Example:
225
- serialbench analyze_performance artifacts/benchmark-results-*/ performance_analysis.json
226
- DESC
227
- def analyze_performance(*args)
228
- if args.length < 2
229
- say 'Error: Need at least one input directory and one output file', :red
230
- say 'Usage: serialbench analyze_performance INPUT_DIRS... OUTPUT_FILE', :yellow
231
- exit 1
232
- end
233
-
234
- output_file = args.pop
235
- input_dirs = args
236
-
237
- say "Analyzing performance from #{input_dirs.length} directories", :green
238
-
239
- begin
240
- results = []
241
-
242
- input_dirs.each do |input_dir|
243
- results_file = File.join(input_dir, 'data', 'results.json')
244
- next unless File.exist?(results_file)
245
-
246
- # Extract platform and ruby version from directory name
247
- match = input_dir.match(/benchmark-results-([^-]+)-ruby-([^\/]+)/)
248
- next unless match
249
-
250
- platform = match[1]
251
- ruby_version = match[2]
252
-
253
- begin
254
- data = JSON.parse(File.read(results_file))
255
-
256
- # Process parsing results
257
- data['parsing']&.each do |format, serializers|
258
- serializers.each do |serializer, sizes|
259
- sizes.each do |size, metrics|
260
- results << {
261
- platform: platform,
262
- ruby_version: ruby_version,
263
- format: format,
264
- serializer: serializer,
265
- size: size,
266
- operation: 'parsing',
267
- time_ms: metrics['average_time'] || 0,
268
- memory_mb: metrics['memory_usage'] || 0,
269
- iterations_per_second: metrics['iterations_per_second'] || 0
270
- }
271
- end
272
- end
273
- end
274
-
275
- # Process generation results
276
- data['generation']&.each do |format, serializers|
277
- serializers.each do |serializer, sizes|
278
- sizes.each do |size, metrics|
279
- results << {
280
- platform: platform,
281
- ruby_version: ruby_version,
282
- format: format,
283
- serializer: serializer,
284
- size: size,
285
- operation: 'generation',
286
- time_ms: metrics['average_time'] || 0,
287
- memory_mb: metrics['memory_usage'] || 0,
288
- iterations_per_second: metrics['iterations_per_second'] || 0
289
- }
290
- end
291
- end
292
- end
293
- rescue JSON::ParserError => e
294
- say "Warning: Could not parse #{results_file}: #{e.message}", :yellow
295
- end
296
- end
297
-
298
- # Generate analysis report
299
- analysis_report = {
300
- 'summary' => 'Cross-platform performance analysis',
301
- 'generated_at' => Time.now.iso8601,
302
- 'total_data_points' => results.length,
303
- 'platforms' => results.map { |r| r[:platform] }.uniq.sort,
304
- 'ruby_versions' => results.map { |r| r[:ruby_version] }.uniq.sort,
305
- 'formats' => results.map { |r| r[:format] }.uniq.sort,
306
- 'serializers' => results.map { |r| r[:serializer] }.uniq.sort,
307
- 'operations' => results.map { |r| r[:operation] }.uniq.sort,
308
- 'data' => results
309
- }
310
-
311
- # Write JSON analysis
312
- File.write(output_file, JSON.pretty_generate(analysis_report))
313
-
314
- say "Performance analysis generated with #{results.length} data points", :green
315
- say "Platforms: #{analysis_report['platforms'].join(', ')}", :cyan
316
- say "Ruby versions: #{analysis_report['ruby_versions'].join(', ')}", :cyan
317
- say "Formats: #{analysis_report['formats'].join(', ')}", :cyan
318
- say "Output saved to: #{output_file}", :green
319
- rescue StandardError => e
320
- say "Error analyzing performance: #{e.message}", :red
321
- exit 1
322
- end
26
+ def self.version
27
+ puts "Serialbench version #{Serialbench::VERSION}"
323
28
  end
324
29
 
325
- desc 'platform_comparison JSON_FILE OUTPUT_FILE', 'Generate platform comparison report from performance analysis'
326
- long_desc <<~DESC
327
- Generate a platform comparison report from performance analysis JSON.
328
-
329
- JSON_FILE should be the output from analyze_performance command.
330
- OUTPUT_FILE will be a JSON file with platform comparison statistics.
331
-
332
- Example:
333
- serialbench platform_comparison performance_analysis.json platform_comparison.json
334
- DESC
335
- def platform_comparison(json_file, output_file)
336
- say "Generating platform comparison from #{json_file}", :green
337
-
338
- unless File.exist?(json_file)
339
- say "JSON file does not exist: #{json_file}", :red
340
- exit 1
341
- end
342
-
343
- begin
344
- # Read the performance analysis JSON
345
- analysis_data = JSON.parse(File.read(json_file))
346
- data_points = analysis_data['data']
347
-
348
- # Group by platform and calculate averages
349
- platform_stats = {}
350
-
351
- data_points.each do |point|
352
- platform = point['platform']
353
- format = point['format']
354
- operation = point['operation']
355
- time = point['time_ms'].to_f
356
-
357
- platform_stats[platform] ||= {}
358
- platform_stats[platform][format] ||= {}
359
- platform_stats[platform][format][operation] ||= []
360
- platform_stats[platform][format][operation] << time
361
- end
362
-
363
- # Calculate averages and generate report
364
- report = {
365
- 'summary' => 'Cross-platform performance comparison',
366
- 'generated_at' => Time.now.iso8601,
367
- 'source_analysis' => json_file,
368
- 'total_platforms' => platform_stats.keys.length,
369
- 'platforms' => {}
370
- }
371
-
372
- platform_stats.each do |platform, formats|
373
- report['platforms'][platform] = {}
374
- formats.each do |format, operations|
375
- report['platforms'][platform][format] = {}
376
- operations.each do |operation, times|
377
- avg_time = times.sum / times.length
378
- report['platforms'][platform][format][operation] = {
379
- 'average_time_ms' => avg_time.round(3),
380
- 'sample_count' => times.length,
381
- 'min_time_ms' => times.min.round(3),
382
- 'max_time_ms' => times.max.round(3),
383
- 'std_deviation' => calculate_std_deviation(times).round(3)
384
- }
385
- end
386
- end
387
- end
388
-
389
- # Write JSON report
390
- File.write(output_file, JSON.pretty_generate(report))
391
-
392
- say "Platform comparison report generated", :green
393
- say "Platforms analyzed: #{platform_stats.keys.sort.join(', ')}", :cyan
394
- say "Output saved to: #{output_file}", :green
395
- rescue StandardError => e
396
- say "Error generating platform comparison: #{e.message}", :red
397
- exit 1
398
- end
399
- end
400
-
401
- private
402
-
403
- def show_available_serializers(formats)
404
- say "\nAvailable serializers:", :cyan
405
-
406
- formats.each do |format|
407
- serializers = Serialbench::Serializers.available_for_format(format)
408
- next if serializers.empty?
409
-
410
- serializer_names = serializers.map do |serializer_class|
411
- serializer = serializer_class.new
412
- "#{serializer.name} v#{serializer.version}"
413
- end
414
-
415
- say " #{format.upcase}: #{serializer_names.join(', ')}", :white
416
- end
417
-
418
- say "\nTest data sizes: small, medium, large", :cyan
419
- say ''
420
- end
421
-
422
- def show_serializers_for_format(format, serializers)
423
- say "#{format.upcase}:", :cyan
424
-
425
- serializers.each do |serializer_class|
426
- serializer = serializer_class.new
427
- features = []
428
- features << 'streaming' if serializer.supports_streaming?
429
- features << 'built-in' if %w[json rexml psych].include?(serializer.name)
430
-
431
- feature_text = features.empty? ? '' : " (#{features.join(', ')})"
432
- say " ✓ #{serializer.name} v#{serializer.version}#{feature_text}", :green
433
- end
434
- end
435
-
436
- def run_selected_benchmarks(runner)
437
- results = { environment: runner.environment_info }
438
-
439
- if options[:parsing_only]
440
- say 'Running parsing benchmarks...', :yellow
441
- results[:parsing] = runner.run_parsing_benchmarks
442
- elsif options[:generation_only]
443
- say 'Running generation benchmarks...', :yellow
444
- results[:generation] = runner.run_generation_benchmarks
445
- elsif options[:streaming_only]
446
- say 'Running streaming benchmarks...', :yellow
447
- results[:streaming] = runner.run_streaming_benchmarks
448
- elsif options[:memory_only]
449
- say 'Running memory benchmarks...', :yellow
450
- results[:memory_usage] = runner.run_memory_benchmarks
30
+ desc 'help [COMMAND]', 'Show help for commands'
31
+ def help(command = nil)
32
+ if command
33
+ super(command)
451
34
  else
452
- say 'Running all benchmarks...', :yellow
453
- results = runner.run_all_benchmarks
454
- end
35
+ puts <<~HELP
36
+ Serialbench - Benchmarking Framework for Ruby Serialization Libraries
455
37
 
456
- results
457
- end
38
+ USAGE:
39
+ serialbench COMMAND [SUBCOMMAND] [OPTIONS]
458
40
 
459
- def save_results(results)
460
- case options[:output_format]
461
- when 'json'
462
- save_json_results(results)
463
- when 'yaml'
464
- save_yaml_results(results)
465
- when 'html'
466
- generate_html_reports(results)
467
- else
468
- # Generate all formats
469
- save_json_results(results)
470
- save_yaml_results(results)
471
- generate_html_reports(results)
472
- end
473
-
474
- show_generated_files
475
- end
41
+ COMMANDS:
42
+ environment Manage benchmark environments (Docker, ASDF, Local)
43
+ benchmark Manage individual benchmark runs
44
+ resultset Manage benchmark resultsets (collections of runs)
45
+ ruby-build Manage Ruby-Build definitions for validation
46
+ version Show version information
47
+ help Show this help message
476
48
 
477
- def save_json_results(results)
478
- FileUtils.mkdir_p('results/data')
49
+ EXAMPLES:
50
+ # Create a Docker environment
51
+ serialbench environment new docker-test docker
479
52
 
480
- # Add Ruby version to results
481
- results[:ruby_version] = RUBY_VERSION
482
- results[:ruby_platform] = RUBY_PLATFORM
483
- results[:timestamp] = Time.now.iso8601
53
+ # Run multi-environment benchmarks
54
+ serialbench environment multi-execute asdf --config=serialbench-asdf.yml
55
+ serialbench environment multi-execute docker --config=serialbench-docker.yml
484
56
 
485
- File.write('results/data/results.json', JSON.pretty_generate(results))
486
- say 'JSON results saved to: results/data/results.json', :green
487
- end
488
-
489
- def save_yaml_results(results)
490
- FileUtils.mkdir_p('results/data')
57
+ # Create and execute a benchmark
58
+ serialbench benchmark create my-benchmark
59
+ serialbench benchmark execute my-benchmark.yml
491
60
 
492
- # Add Ruby version to results
493
- results[:ruby_version] = RUBY_VERSION
494
- results[:ruby_platform] = RUBY_PLATFORM
495
- results[:timestamp] = Time.now.iso8601
496
-
497
- File.write('results/data/results.yaml', results.to_yaml)
498
- say 'YAML results saved to: results/data/results.yaml', :green
499
- end
61
+ # Create a result set for comparison
62
+ serialbench resultset create comparison-set
63
+ serialbench resultset add-result comparison-set results/my-benchmark
500
64
 
501
- def generate_html_reports(results)
502
- say 'Generating reports...', :yellow
503
- report_files = Serialbench.generate_reports(results)
65
+ # Generate static sites
66
+ serialbench benchmark build-site results/my-benchmark
67
+ serialbench resultset build-site resultsets/comparison-set
504
68
 
505
- say 'Reports generated:', :green
506
- say " HTML: #{report_files[:html]}", :white
507
- say " CSS: #{report_files[:css]}", :white
508
- end
509
-
510
- def show_generated_files
511
- case options[:output_format]
512
- when 'json'
513
- say 'Files generated:', :cyan
514
- say ' JSON: results/data/results.json', :white
515
- when 'yaml'
516
- say 'Files generated:', :cyan
517
- say ' YAML: results/data/results.yaml', :white
518
- when 'html'
519
- say 'Files generated:', :cyan
520
- say ' HTML: results/reports/benchmark_report.html', :white
521
- say ' Charts: results/charts/*.svg', :white
522
- else
523
- say 'Files generated:', :cyan
524
- say ' JSON: results/data/results.json', :white
525
- say ' YAML: results/data/results.yaml', :white
526
- say ' HTML: results/reports/benchmark_report.html', :white
527
- say ' Charts: results/charts/*.svg', :white
69
+ For detailed help on any command, use:
70
+ serialbench COMMAND help
71
+ HELP
528
72
  end
529
73
  end
530
74
 
531
- def show_summary(results)
532
- return unless results[:parsing] || results[:generation]
533
-
534
- say "\n" + '=' * 50, :green
535
- say 'BENCHMARK SUMMARY', :green
536
- say '=' * 50, :green
537
-
538
- show_parsing_summary(results[:parsing]) if results[:parsing]
539
-
540
- show_generation_summary(results[:generation]) if results[:generation]
541
-
542
- return unless results[:memory_usage]
543
-
544
- show_memory_summary(results[:memory_usage])
75
+ # Handle unknown commands gracefully
76
+ def method_missing(method_name, *args)
77
+ puts "Unknown command: #{method_name}"
78
+ puts ''
79
+ help
80
+ exit 1
545
81
  end
546
82
 
547
- def show_parsing_summary(parsing_results)
548
- say "\nParsing Performance (operations/second):", :cyan
549
-
550
- %i[small medium large].each do |size|
551
- next unless parsing_results[size]
552
-
553
- say "\n #{size.capitalize} files:", :yellow
554
-
555
- # Flatten the nested structure and sort by performance
556
- flattened_results = []
557
- parsing_results[size].each do |format, serializers|
558
- serializers.each do |serializer_name, data|
559
- flattened_results << ["#{format}/#{serializer_name}", data]
560
- end
561
- end
562
-
563
- sorted_results = flattened_results.sort_by { |_, data| -data[:iterations_per_second] }
564
-
565
- sorted_results.each do |serializer_name, data|
566
- ops_per_sec = data[:iterations_per_second].round(2)
567
- say " #{serializer_name}: #{ops_per_sec} ops/sec", :white
568
- end
569
- end
570
- end
571
-
572
- def show_generation_summary(generation_results)
573
- say "\nGeneration Performance (operations/second):", :cyan
574
-
575
- %i[small medium large].each do |size|
576
- next unless generation_results[size]
577
-
578
- say "\n #{size.capitalize} files:", :yellow
579
-
580
- # Flatten the nested structure and sort by performance
581
- flattened_results = []
582
- generation_results[size].each do |format, serializers|
583
- serializers.each do |serializer_name, data|
584
- flattened_results << ["#{format}/#{serializer_name}", data]
585
- end
586
- end
587
-
588
- sorted_results = flattened_results.sort_by { |_, data| -data[:iterations_per_second] }
589
-
590
- sorted_results.each do |serializer_name, data|
591
- ops_per_sec = data[:iterations_per_second].round(2)
592
- say " #{serializer_name}: #{ops_per_sec} ops/sec", :white
593
- end
594
- end
595
- end
596
-
597
- def show_memory_summary(memory_results)
598
- say "\nMemory Usage (MB):", :cyan
599
-
600
- %i[small medium large].each do |size|
601
- next unless memory_results[size]
602
-
603
- say "\n #{size.capitalize} files:", :yellow
604
-
605
- # Flatten the nested structure and sort by memory usage (ascending)
606
- flattened_results = []
607
- memory_results[size].each do |format, serializers|
608
- serializers.each do |serializer_name, data|
609
- flattened_results << ["#{format}/#{serializer_name}", data]
610
- end
611
- end
612
-
613
- sorted_results = flattened_results.sort_by { |_, data| data[:allocated_memory] }
614
-
615
- sorted_results.each do |serializer_name, data|
616
- memory_mb = (data[:allocated_memory] / 1024.0 / 1024.0).round(2)
617
- say " #{serializer_name}: #{memory_mb} MB", :white
618
- end
619
- end
620
- end
621
-
622
- def calculate_std_deviation(values)
623
- return 0.0 if values.length <= 1
624
-
625
- mean = values.sum.to_f / values.length
626
- variance = values.map { |v| (v - mean)**2 }.sum / values.length
627
- Math.sqrt(variance)
83
+ def respond_to_missing?(method_name, include_private = false)
84
+ false
628
85
  end
629
86
  end
630
87
  end