serialbench 0.1.1 → 0.1.3
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.
- checksums.yaml +4 -4
- data/.github/workflows/benchmark.yml +273 -220
- data/.github/workflows/rake.yml +26 -0
- data/.github/workflows/windows-debug.yml +171 -0
- data/.gitignore +32 -0
- data/.rubocop.yml +1 -0
- data/.rubocop_todo.yml +274 -0
- data/Gemfile +14 -1
- data/README.adoc +292 -1118
- data/Rakefile +0 -55
- data/config/benchmarks/full.yml +29 -0
- data/config/benchmarks/short.yml +26 -0
- data/config/environments/asdf-ruby-3.2.yml +8 -0
- data/config/environments/asdf-ruby-3.3.yml +8 -0
- data/config/environments/docker-ruby-3.0.yml +9 -0
- data/config/environments/docker-ruby-3.1.yml +9 -0
- data/config/environments/docker-ruby-3.2.yml +9 -0
- data/config/environments/docker-ruby-3.3.yml +9 -0
- data/config/environments/docker-ruby-3.4.yml +9 -0
- data/data/schemas/result.yml +29 -0
- data/docker/Dockerfile.alpine +33 -0
- data/docker/{Dockerfile.benchmark → Dockerfile.ubuntu} +4 -3
- data/docker/README.md +2 -2
- data/docs/PLATFORM_VALIDATION_FIX.md +79 -0
- data/docs/SYCK_YAML_FIX.md +91 -0
- data/docs/WEBSITE_COMPLETION_PLAN.md +440 -0
- data/docs/WINDOWS_LIBXML_FIX.md +136 -0
- data/docs/WINDOWS_SETUP.md +122 -0
- data/exe/serialbench +1 -1
- data/lib/serialbench/benchmark_runner.rb +261 -423
- data/lib/serialbench/cli/base_cli.rb +51 -0
- data/lib/serialbench/cli/benchmark_cli.rb +453 -0
- data/lib/serialbench/cli/environment_cli.rb +181 -0
- data/lib/serialbench/cli/resultset_cli.rb +261 -0
- data/lib/serialbench/cli/ruby_build_cli.rb +225 -0
- data/lib/serialbench/cli/validate_cli.rb +88 -0
- data/lib/serialbench/cli.rb +61 -600
- data/lib/serialbench/config_manager.rb +129 -0
- data/lib/serialbench/models/benchmark_config.rb +75 -0
- data/lib/serialbench/models/benchmark_result.rb +81 -0
- data/lib/serialbench/models/environment_config.rb +72 -0
- data/lib/serialbench/models/platform.rb +111 -0
- data/lib/serialbench/models/result.rb +80 -0
- data/lib/serialbench/models/result_set.rb +79 -0
- data/lib/serialbench/models/result_store.rb +108 -0
- data/lib/serialbench/models.rb +54 -0
- data/lib/serialbench/ruby_build_manager.rb +149 -0
- data/lib/serialbench/runners/asdf_runner.rb +296 -0
- data/lib/serialbench/runners/base.rb +32 -0
- data/lib/serialbench/runners/docker_runner.rb +140 -0
- data/lib/serialbench/runners/local_runner.rb +71 -0
- data/lib/serialbench/serializers/base_serializer.rb +9 -17
- data/lib/serialbench/serializers/json/base_json_serializer.rb +4 -4
- data/lib/serialbench/serializers/json/json_serializer.rb +0 -2
- data/lib/serialbench/serializers/json/oj_serializer.rb +0 -2
- data/lib/serialbench/serializers/json/rapidjson_serializer.rb +1 -1
- data/lib/serialbench/serializers/json/yajl_serializer.rb +0 -2
- data/lib/serialbench/serializers/toml/base_toml_serializer.rb +5 -5
- data/lib/serialbench/serializers/toml/toml_rb_serializer.rb +1 -3
- data/lib/serialbench/serializers/toml/tomlib_serializer.rb +1 -3
- data/lib/serialbench/serializers/toml/tomlrb_serializer.rb +56 -0
- data/lib/serialbench/serializers/xml/base_xml_serializer.rb +4 -9
- data/lib/serialbench/serializers/xml/libxml_serializer.rb +4 -10
- data/lib/serialbench/serializers/xml/nokogiri_serializer.rb +2 -4
- data/lib/serialbench/serializers/xml/oga_serializer.rb +4 -10
- data/lib/serialbench/serializers/xml/ox_serializer.rb +2 -4
- data/lib/serialbench/serializers/xml/rexml_serializer.rb +3 -5
- data/lib/serialbench/serializers/yaml/base_yaml_serializer.rb +5 -1
- data/lib/serialbench/serializers/yaml/psych_serializer.rb +1 -1
- data/lib/serialbench/serializers/yaml/syck_serializer.rb +60 -23
- data/lib/serialbench/serializers.rb +23 -6
- data/lib/serialbench/site_generator.rb +283 -0
- data/lib/serialbench/templates/assets/css/benchmark_report.css +535 -0
- data/lib/serialbench/templates/assets/css/format_based.css +474 -0
- data/lib/serialbench/templates/assets/css/themes.css +589 -0
- data/lib/serialbench/templates/assets/js/chart_helpers.js +411 -0
- data/lib/serialbench/templates/assets/js/dashboard.js +795 -0
- data/lib/serialbench/templates/assets/js/navigation.js +142 -0
- data/lib/serialbench/templates/base.liquid +49 -0
- data/lib/serialbench/templates/format_based.liquid +507 -0
- data/lib/serialbench/templates/partials/chart_section.liquid +4 -0
- data/lib/serialbench/version.rb +1 -1
- data/lib/serialbench/yaml_validator.rb +36 -0
- data/lib/serialbench.rb +2 -31
- data/serialbench.gemspec +15 -3
- metadata +106 -25
- data/.github/workflows/ci.yml +0 -74
- data/.github/workflows/docker.yml +0 -246
- data/config/ci.yml +0 -22
- data/config/full.yml +0 -30
- data/docker/run-benchmarks.sh +0 -356
- data/lib/serialbench/chart_generator.rb +0 -821
- data/lib/serialbench/result_formatter.rb +0 -182
- data/lib/serialbench/result_merger.rb +0 -1201
- data/lib/serialbench/serializers/xml/base_parser.rb +0 -69
- data/lib/serialbench/serializers/xml/libxml_parser.rb +0 -98
- data/lib/serialbench/serializers/xml/nokogiri_parser.rb +0 -111
- data/lib/serialbench/serializers/xml/oga_parser.rb +0 -85
- data/lib/serialbench/serializers/xml/ox_parser.rb +0 -64
- data/lib/serialbench/serializers/xml/rexml_parser.rb +0 -129
|
@@ -7,7 +7,7 @@ module Serialbench
|
|
|
7
7
|
module Yaml
|
|
8
8
|
# Base class for YAML serializers
|
|
9
9
|
class BaseYamlSerializer < BaseSerializer
|
|
10
|
-
def format
|
|
10
|
+
def self.format
|
|
11
11
|
:yaml
|
|
12
12
|
end
|
|
13
13
|
|
|
@@ -41,6 +41,10 @@ module Serialbench
|
|
|
41
41
|
raise NotImplementedError, 'Streaming not supported by this YAML serializer'
|
|
42
42
|
end
|
|
43
43
|
|
|
44
|
+
def supports_generation?
|
|
45
|
+
true
|
|
46
|
+
end
|
|
47
|
+
|
|
44
48
|
private
|
|
45
49
|
|
|
46
50
|
def require_library(library_name)
|
|
@@ -11,41 +11,61 @@ module Serialbench
|
|
|
11
11
|
end
|
|
12
12
|
|
|
13
13
|
def version
|
|
14
|
-
|
|
14
|
+
require 'syck'
|
|
15
|
+
# Try to get version from gem specification
|
|
16
|
+
spec = Gem.loaded_specs['syck']
|
|
17
|
+
return spec.version.to_s if spec
|
|
15
18
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
return spec.version.to_s if spec
|
|
21
|
-
|
|
22
|
-
# Fallback to a default version if no gem spec found
|
|
23
|
-
'1.0.0'
|
|
24
|
-
rescue
|
|
25
|
-
'N/A'
|
|
26
|
-
end
|
|
19
|
+
# Fallback to a default version if no gem spec found
|
|
20
|
+
'1.0.0'
|
|
21
|
+
rescue StandardError
|
|
22
|
+
'N/A'
|
|
27
23
|
end
|
|
28
24
|
|
|
29
25
|
def available?
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
26
|
+
require 'syck'
|
|
27
|
+
# Verify that Syck module and methods are actually available
|
|
28
|
+
return false unless defined?(Syck) && Syck.respond_to?(:dump) && Syck.respond_to?(:load)
|
|
29
|
+
|
|
30
|
+
# Check for known problematic configurations
|
|
31
|
+
if problematic_environment?
|
|
32
|
+
warn_about_segfault_issue
|
|
33
|
+
return false
|
|
36
34
|
end
|
|
35
|
+
|
|
36
|
+
true
|
|
37
|
+
rescue LoadError
|
|
38
|
+
false
|
|
37
39
|
end
|
|
38
40
|
|
|
39
41
|
def parse(yaml_string)
|
|
40
42
|
return nil unless available?
|
|
41
|
-
|
|
42
|
-
|
|
43
|
+
|
|
44
|
+
begin
|
|
45
|
+
require 'syck'
|
|
46
|
+
Syck.load(yaml_string)
|
|
47
|
+
rescue StandardError => e
|
|
48
|
+
if e.message.include?('Segmentation fault') || e.is_a?(SystemExit)
|
|
49
|
+
handle_segfault_error
|
|
50
|
+
return nil
|
|
51
|
+
end
|
|
52
|
+
raise e
|
|
53
|
+
end
|
|
43
54
|
end
|
|
44
55
|
|
|
45
|
-
def generate(object,
|
|
56
|
+
def generate(object, _options = {})
|
|
46
57
|
return nil unless available?
|
|
47
|
-
|
|
48
|
-
|
|
58
|
+
|
|
59
|
+
begin
|
|
60
|
+
require 'syck'
|
|
61
|
+
Syck.dump(object)
|
|
62
|
+
rescue StandardError => e
|
|
63
|
+
if e.message.include?('Segmentation fault') || e.is_a?(SystemExit)
|
|
64
|
+
handle_segfault_error
|
|
65
|
+
return nil
|
|
66
|
+
end
|
|
67
|
+
raise e
|
|
68
|
+
end
|
|
49
69
|
end
|
|
50
70
|
|
|
51
71
|
def supports_streaming?
|
|
@@ -59,6 +79,23 @@ module Serialbench
|
|
|
59
79
|
def description
|
|
60
80
|
'Legacy YAML parser (Ruby < 1.9.3)'
|
|
61
81
|
end
|
|
82
|
+
|
|
83
|
+
def problematic_environment?
|
|
84
|
+
# Ruby 3.1+ has issues with Syck
|
|
85
|
+
(Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('3.1.0')) &&
|
|
86
|
+
(Gem::Version.new(version) >= Gem::Version.new('1.4.0'))
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def warn_about_segfault_issue
|
|
90
|
+
puts "⚠️ WARNING: The Syck YAML serializer is known to cause segmentation faults with Ruby #{RUBY_VERSION}"
|
|
91
|
+
puts ' This is a known issue with the syck gem on newer Ruby versions systems.'
|
|
92
|
+
puts ' Skipping Syck benchmarks to prevent crashes. See README for more details.'
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def handle_segfault_error
|
|
96
|
+
puts '❌ ERROR: Syck YAML serializer encountered a segmentation fault'
|
|
97
|
+
puts " This is a known issue on Ruby #{RUBY_VERSION}. Skipping remaining Syck tests."
|
|
98
|
+
end
|
|
62
99
|
end
|
|
63
100
|
end
|
|
64
101
|
end
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative 'serializers/base_serializer'
|
|
4
|
+
require_relative 'models/benchmark_result'
|
|
4
5
|
|
|
5
6
|
# XML Serializers
|
|
6
7
|
require_relative 'serializers/xml/base_xml_serializer'
|
|
@@ -26,11 +27,12 @@ require_relative 'serializers/yaml/syck_serializer'
|
|
|
26
27
|
require_relative 'serializers/toml/base_toml_serializer'
|
|
27
28
|
require_relative 'serializers/toml/toml_rb_serializer'
|
|
28
29
|
require_relative 'serializers/toml/tomlib_serializer'
|
|
30
|
+
require_relative 'serializers/toml/tomlrb_serializer'
|
|
29
31
|
|
|
30
32
|
module Serialbench
|
|
31
33
|
module Serializers
|
|
32
34
|
# Registry of all available serializers
|
|
33
|
-
|
|
35
|
+
REGISTER = {
|
|
34
36
|
xml: [
|
|
35
37
|
Xml::RexmlSerializer,
|
|
36
38
|
Xml::OxSerializer,
|
|
@@ -50,24 +52,39 @@ module Serialbench
|
|
|
50
52
|
],
|
|
51
53
|
toml: [
|
|
52
54
|
Toml::TomlRbSerializer,
|
|
53
|
-
Toml::TomlibSerializer
|
|
55
|
+
Toml::TomlibSerializer,
|
|
56
|
+
Toml::TomlrbSerializer
|
|
54
57
|
]
|
|
55
58
|
}.freeze
|
|
56
59
|
|
|
57
60
|
def self.all
|
|
58
|
-
|
|
61
|
+
REGISTER.values.flatten.map(&:instance)
|
|
59
62
|
end
|
|
60
63
|
|
|
61
64
|
def self.for_format(format)
|
|
62
|
-
|
|
65
|
+
REGISTER[format.to_sym]&.map(&:instance) || []
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def self.information
|
|
69
|
+
return @information if @information
|
|
70
|
+
|
|
71
|
+
@information = available.map do |serializer_singleton|
|
|
72
|
+
Models::SerializerInformation.new(
|
|
73
|
+
name: serializer_singleton.name,
|
|
74
|
+
format: serializer_singleton.format.to_s,
|
|
75
|
+
version: serializer_singleton.version
|
|
76
|
+
)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
@information
|
|
63
80
|
end
|
|
64
81
|
|
|
65
82
|
def self.available_for_format(format)
|
|
66
|
-
for_format(format).select
|
|
83
|
+
for_format(format).select(&:available?)
|
|
67
84
|
end
|
|
68
85
|
|
|
69
86
|
def self.available
|
|
70
|
-
all.select
|
|
87
|
+
all.select(&:available?)
|
|
71
88
|
end
|
|
72
89
|
end
|
|
73
90
|
end
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'fileutils'
|
|
4
|
+
require 'erb'
|
|
5
|
+
require 'json'
|
|
6
|
+
require 'liquid'
|
|
7
|
+
require 'yaml'
|
|
8
|
+
|
|
9
|
+
module Serialbench
|
|
10
|
+
# Unified site generator for creating static HTML sites from benchmark results
|
|
11
|
+
class SiteGenerator
|
|
12
|
+
TEMPLATE_DIR = File.join(__dir__, 'templates')
|
|
13
|
+
|
|
14
|
+
attr_reader :output_path, :result, :resultset
|
|
15
|
+
|
|
16
|
+
def initialize(output_path:, result: nil, resultset: nil)
|
|
17
|
+
@output_path = File.expand_path(output_path)
|
|
18
|
+
@result = result if result
|
|
19
|
+
@resultset = resultset if resultset
|
|
20
|
+
setup_liquid_environment
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def self.generate_for_result(result, output_path)
|
|
24
|
+
generator = new(output_path: output_path, result: result)
|
|
25
|
+
generator.generate_site
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def self.generate_for_resultset(resultset, output_path)
|
|
29
|
+
generator = new(output_path: output_path, resultset: resultset)
|
|
30
|
+
generator.generate_site
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def generate_site
|
|
34
|
+
target_name = @result ? @result.environment_config.name : @resultset.name
|
|
35
|
+
|
|
36
|
+
puts "🏗️ Generating HTML site for #{@result ? 'run' : 'resultset'}: #{target_name}"
|
|
37
|
+
puts "Output: #{@output_path}"
|
|
38
|
+
|
|
39
|
+
# Transform data for dashboard.js compatibility
|
|
40
|
+
data = if @result
|
|
41
|
+
transform_result_for_dashboard(@result)
|
|
42
|
+
else
|
|
43
|
+
transform_resultset_for_dashboard(@resultset)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
prepare_output_directory
|
|
47
|
+
render_site(
|
|
48
|
+
{
|
|
49
|
+
'data' => JSON.generate(data),
|
|
50
|
+
'kind' => @result ? 'run' : 'resultset'
|
|
51
|
+
},
|
|
52
|
+
'format_based.liquid'
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
# Export raw data files for download
|
|
56
|
+
export_raw_data
|
|
57
|
+
|
|
58
|
+
puts "✅ Site generated successfully at: #{@output_path}"
|
|
59
|
+
@output_path
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
private
|
|
63
|
+
|
|
64
|
+
def setup_liquid_environment
|
|
65
|
+
@liquid_env = Liquid::Environment.new
|
|
66
|
+
@liquid_env.file_system = Liquid::LocalFileSystem.new(TEMPLATE_DIR)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def prepare_output_directory
|
|
70
|
+
if Dir.exist?(@output_path)
|
|
71
|
+
puts 'Cleaning existing output directory...'
|
|
72
|
+
FileUtils.rm_rf(Dir.glob(File.join(@output_path, '*')))
|
|
73
|
+
else
|
|
74
|
+
FileUtils.mkdir_p(@output_path)
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def render_site(template_data, template_name)
|
|
79
|
+
# Load and render content template
|
|
80
|
+
content_template = load_template(template_name)
|
|
81
|
+
content = content_template.render(template_data)
|
|
82
|
+
|
|
83
|
+
# Load and render base template
|
|
84
|
+
base_template = load_template('base.liquid')
|
|
85
|
+
html = base_template.render(template_data.merge('content' => content))
|
|
86
|
+
|
|
87
|
+
# Write HTML file
|
|
88
|
+
write_file(html, 'index.html')
|
|
89
|
+
|
|
90
|
+
# Copy assets
|
|
91
|
+
copy_assets
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def load_template(template_name)
|
|
95
|
+
template_path = File.join(TEMPLATE_DIR, template_name)
|
|
96
|
+
template_content = File.read(template_path)
|
|
97
|
+
Liquid::Template.parse(template_content)
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def write_file(content, filename)
|
|
101
|
+
FileUtils.mkdir_p(@output_path)
|
|
102
|
+
File.write(File.join(@output_path, filename), content)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def copy_assets
|
|
106
|
+
assets_source = File.join(TEMPLATE_DIR, 'assets')
|
|
107
|
+
assets_dest = File.join(@output_path, 'assets')
|
|
108
|
+
|
|
109
|
+
return unless Dir.exist?(assets_source)
|
|
110
|
+
|
|
111
|
+
FileUtils.cp_r(assets_source, assets_dest)
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# Transform a single Result into dashboard-compatible format
|
|
115
|
+
# Dashboard expects: { combined_results: {...}, environments: {...}, metadata: {...} }
|
|
116
|
+
def transform_result_for_dashboard(result)
|
|
117
|
+
env_key = "env-#{result.platform.ruby_version}"
|
|
118
|
+
|
|
119
|
+
# Build combined_results structure
|
|
120
|
+
combined_results = build_combined_results(result, env_key)
|
|
121
|
+
|
|
122
|
+
# Build environments structure
|
|
123
|
+
environments = {
|
|
124
|
+
env_key => {
|
|
125
|
+
'ruby_version' => result.platform.ruby_version,
|
|
126
|
+
'ruby_platform' => result.platform.ruby_platform || "#{result.platform.os}-#{result.platform.arch}",
|
|
127
|
+
'os' => result.platform.os,
|
|
128
|
+
'arch' => result.platform.arch,
|
|
129
|
+
'source_file' => result.metadata.environment_config_path,
|
|
130
|
+
'timestamp' => result.metadata.created_at
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
{
|
|
135
|
+
'combined_results' => combined_results,
|
|
136
|
+
'environments' => environments,
|
|
137
|
+
'metadata' => {
|
|
138
|
+
'generated_at' => Time.now.iso8601
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# Transform a ResultSet (collection of Results) into dashboard-compatible format
|
|
144
|
+
# Combines all results into a single dashboard structure
|
|
145
|
+
def transform_resultset_for_dashboard(resultset)
|
|
146
|
+
combined_results = {}
|
|
147
|
+
environments = {}
|
|
148
|
+
|
|
149
|
+
resultset.results.each do |result|
|
|
150
|
+
# Create unique env key for this result
|
|
151
|
+
env_key = "#{result.platform.os}-#{result.platform.arch}-ruby-#{result.platform.ruby_version}"
|
|
152
|
+
|
|
153
|
+
# Merge this result's data into combined_results
|
|
154
|
+
result_combined = build_combined_results(result, env_key)
|
|
155
|
+
merge_combined_results!(combined_results, result_combined)
|
|
156
|
+
|
|
157
|
+
# Add environment info
|
|
158
|
+
environments[env_key] = {
|
|
159
|
+
'ruby_version' => result.platform.ruby_version,
|
|
160
|
+
'ruby_platform' => result.platform.ruby_platform || "#{result.platform.os}-#{result.platform.arch}",
|
|
161
|
+
'os' => result.platform.os,
|
|
162
|
+
'arch' => result.platform.arch,
|
|
163
|
+
'source_file' => result.metadata.environment_config_path,
|
|
164
|
+
'timestamp' => result.metadata.created_at
|
|
165
|
+
}
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
{
|
|
169
|
+
'combined_results' => combined_results,
|
|
170
|
+
'environments' => environments,
|
|
171
|
+
'metadata' => {
|
|
172
|
+
'resultset_name' => resultset.name,
|
|
173
|
+
'resultset_description' => resultset.description,
|
|
174
|
+
'total_runs' => resultset.results.size,
|
|
175
|
+
'generated_at' => Time.now.iso8601
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
# Deep merge results from multiple runs
|
|
181
|
+
def merge_combined_results!(target, source)
|
|
182
|
+
source.each do |operation, sizes|
|
|
183
|
+
target[operation] ||= {}
|
|
184
|
+
sizes.each do |size, formats|
|
|
185
|
+
target[operation][size] ||= {}
|
|
186
|
+
formats.each do |format, serializers|
|
|
187
|
+
target[operation][size][format] ||= {}
|
|
188
|
+
serializers.each do |serializer, envs|
|
|
189
|
+
target[operation][size][format][serializer] ||= {}
|
|
190
|
+
target[operation][size][format][serializer].merge!(envs)
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
def build_combined_results(result, env_key)
|
|
198
|
+
combined = {}
|
|
199
|
+
|
|
200
|
+
%w[parsing generation streaming].each do |operation|
|
|
201
|
+
combined[operation] = {}
|
|
202
|
+
|
|
203
|
+
operation_results = result.benchmark_result.send(operation)
|
|
204
|
+
next if operation_results.nil? || operation_results.empty?
|
|
205
|
+
|
|
206
|
+
operation_results.each do |perf|
|
|
207
|
+
size = perf.data_size
|
|
208
|
+
format = perf.format
|
|
209
|
+
serializer = perf.adapter
|
|
210
|
+
|
|
211
|
+
combined[operation][size] ||= {}
|
|
212
|
+
combined[operation][size][format] ||= {}
|
|
213
|
+
combined[operation][size][format][serializer] ||= {}
|
|
214
|
+
combined[operation][size][format][serializer][env_key] = {
|
|
215
|
+
'iterations_per_second' => perf.iterations_per_second,
|
|
216
|
+
'time_per_iteration' => perf.time_per_iteration
|
|
217
|
+
}
|
|
218
|
+
end
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
# Handle memory separately
|
|
222
|
+
if result.benchmark_result.memory && !result.benchmark_result.memory.empty?
|
|
223
|
+
combined['memory'] = {}
|
|
224
|
+
|
|
225
|
+
result.benchmark_result.memory.each do |mem|
|
|
226
|
+
size = mem.data_size
|
|
227
|
+
format = mem.format
|
|
228
|
+
serializer = mem.adapter
|
|
229
|
+
|
|
230
|
+
combined['memory'][size] ||= {}
|
|
231
|
+
combined['memory'][size][format] ||= {}
|
|
232
|
+
combined['memory'][size][format][serializer] ||= {}
|
|
233
|
+
combined['memory'][size][format][serializer][env_key] = {
|
|
234
|
+
'allocated_memory' => mem.allocated_memory,
|
|
235
|
+
'retained_memory' => mem.retained_memory
|
|
236
|
+
}
|
|
237
|
+
end
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
combined
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
# Export raw data files for download
|
|
244
|
+
def export_raw_data
|
|
245
|
+
data_dir = File.join(@output_path, 'data')
|
|
246
|
+
FileUtils.mkdir_p(data_dir)
|
|
247
|
+
|
|
248
|
+
if @result
|
|
249
|
+
# Export single result
|
|
250
|
+
export_single_result(@result, data_dir)
|
|
251
|
+
elsif @resultset
|
|
252
|
+
# Export all results in resultset plus the complete resultset
|
|
253
|
+
export_resultset(@resultset, data_dir)
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
puts "📦 Raw data files exported to: #{data_dir}"
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
def export_single_result(result, data_dir)
|
|
260
|
+
# Create filename based on platform info
|
|
261
|
+
env_key = "#{result.platform.os}-#{result.platform.arch}-ruby-#{result.platform.ruby_version}"
|
|
262
|
+
filename = "#{env_key}.yaml"
|
|
263
|
+
filepath = File.join(data_dir, filename)
|
|
264
|
+
|
|
265
|
+
# Write result as YAML
|
|
266
|
+
File.write(filepath, result.to_yaml)
|
|
267
|
+
puts " 📄 Exported: #{filename}"
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
def export_resultset(resultset, data_dir)
|
|
271
|
+
# Export each individual result
|
|
272
|
+
resultset.results.each do |result|
|
|
273
|
+
export_single_result(result, data_dir)
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
# Export complete resultset
|
|
277
|
+
resultset_filename = 'resultset.yaml'
|
|
278
|
+
resultset_filepath = File.join(data_dir, resultset_filename)
|
|
279
|
+
File.write(resultset_filepath, resultset.to_yaml)
|
|
280
|
+
puts " 📄 Exported: #{resultset_filename} (complete dataset)"
|
|
281
|
+
end
|
|
282
|
+
end
|
|
283
|
+
end
|