serialbench 0.1.2 → 0.2.0
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 +294 -229
- data/.github/workflows/rake.yml +11 -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 +15 -1
- data/README.adoc +41 -0
- data/data/schemas/result.yml +29 -0
- 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/lib/serialbench/benchmark_runner.rb +3 -3
- data/lib/serialbench/cli/benchmark_cli.rb +74 -1
- data/lib/serialbench/cli/environment_cli.rb +3 -3
- data/lib/serialbench/cli/resultset_cli.rb +112 -26
- data/lib/serialbench/cli/ruby_build_cli.rb +75 -88
- data/lib/serialbench/cli/validate_cli.rb +88 -0
- data/lib/serialbench/cli.rb +6 -2
- data/lib/serialbench/config_manager.rb +15 -26
- data/lib/serialbench/models/benchmark_config.rb +12 -0
- data/lib/serialbench/models/benchmark_result.rb +39 -3
- data/lib/serialbench/models/environment_config.rb +3 -2
- data/lib/serialbench/models/platform.rb +61 -6
- data/lib/serialbench/models/result.rb +28 -1
- data/lib/serialbench/models/result_set.rb +8 -0
- data/lib/serialbench/ruby_build_manager.rb +19 -23
- data/lib/serialbench/runners/asdf_runner.rb +1 -1
- data/lib/serialbench/runners/docker_runner.rb +2 -4
- data/lib/serialbench/runners/local_runner.rb +71 -0
- data/lib/serialbench/serializers/base_serializer.rb +1 -1
- data/lib/serialbench/serializers/json/rapidjson_serializer.rb +1 -5
- data/lib/serialbench/serializers/toml/base_toml_serializer.rb +0 -2
- data/lib/serialbench/serializers/toml/toml_rb_serializer.rb +1 -1
- data/lib/serialbench/serializers/toml/tomlib_serializer.rb +1 -1
- data/lib/serialbench/serializers/xml/base_xml_serializer.rb +6 -1
- data/lib/serialbench/serializers/xml/leptris_serializer.rb +167 -0
- data/lib/serialbench/serializers/xml/libxml_serializer.rb +13 -8
- data/lib/serialbench/serializers/xml/nokogiri_serializer.rb +11 -2
- data/lib/serialbench/serializers/xml/oga_serializer.rb +8 -8
- data/lib/serialbench/serializers/xml/ox_serializer.rb +2 -2
- data/lib/serialbench/serializers/xml/rexml_serializer.rb +3 -3
- data/lib/serialbench/serializers/yaml/base_yaml_serializer.rb +1 -3
- data/lib/serialbench/serializers/yaml/psych_serializer.rb +1 -5
- data/lib/serialbench/serializers/yaml/syck_serializer.rb +1 -1
- data/lib/serialbench/serializers.rb +5 -3
- data/lib/serialbench/site_generator.rb +234 -2
- data/lib/serialbench/templates/assets/css/format_based.css +1 -53
- data/lib/serialbench/templates/assets/css/themes.css +5 -4
- data/lib/serialbench/templates/assets/js/chart_helpers.js +44 -14
- data/lib/serialbench/templates/assets/js/dashboard.js +14 -15
- data/lib/serialbench/templates/format_based.liquid +480 -252
- data/lib/serialbench/version.rb +1 -1
- data/lib/serialbench/yaml_validator.rb +36 -0
- data/serialbench.gemspec +11 -2
- data/site/.gitignore +5 -0
- data/site/README.md +32 -0
- data/site/astro.config.mjs +12 -0
- data/site/package-lock.json +6160 -0
- data/site/package.json +22 -0
- data/site/public/favicon.svg +4 -0
- data/site/scripts/gen-sample-data.mjs +154 -0
- data/site/src/components/AvailabilityMatrix.astro +61 -0
- data/site/src/components/CalibratedLeaderboard.vue +134 -0
- data/site/src/components/CitationBox.vue +50 -0
- data/site/src/components/DashboardConsole.vue +193 -0
- data/site/src/components/FeaturesTable.astro +97 -0
- data/site/src/components/MemoryPanel.vue +44 -0
- data/site/src/components/OpsChart.vue +135 -0
- data/site/src/config.ts +18 -0
- data/site/src/layouts/Layout.astro +85 -0
- data/site/src/lib/channels.ts +24 -0
- data/site/src/lib/dashboard.ts +269 -0
- data/site/src/pages/index.astro +44 -0
- data/site/src/pages/library/[slug].astro +128 -0
- data/site/src/pages/methodology.astro +55 -0
- data/site/src/styles/global.css +103 -0
- data/site/tsconfig.json +5 -0
- metadata +58 -23
- data/.github/workflows/ci.yml +0 -74
- data/.github/workflows/docker.yml +0 -272
|
@@ -41,7 +41,7 @@ module Serialbench
|
|
|
41
41
|
|
|
42
42
|
indent = options.fetch(:indent, 0)
|
|
43
43
|
output = String.new
|
|
44
|
-
if indent
|
|
44
|
+
if indent.positive?
|
|
45
45
|
document.write(output, indent)
|
|
46
46
|
else
|
|
47
47
|
document.write(output)
|
|
@@ -120,7 +120,7 @@ module Serialbench
|
|
|
120
120
|
@result = nil
|
|
121
121
|
end
|
|
122
122
|
|
|
123
|
-
def start_element(
|
|
123
|
+
def start_element(_uri, _localname, qname, attributes)
|
|
124
124
|
@elements_processed += 1
|
|
125
125
|
@block&.call(:start_element, { name: qname, attributes: attributes })
|
|
126
126
|
end
|
|
@@ -132,7 +132,7 @@ module Serialbench
|
|
|
132
132
|
@block&.call(:text, text)
|
|
133
133
|
end
|
|
134
134
|
|
|
135
|
-
def end_element(
|
|
135
|
+
def end_element(_uri, _localname, qname)
|
|
136
136
|
@block&.call(:end_element, { name: qname })
|
|
137
137
|
end
|
|
138
138
|
end
|
|
@@ -31,15 +31,11 @@ module Serialbench
|
|
|
31
31
|
end
|
|
32
32
|
end
|
|
33
33
|
|
|
34
|
-
def generate(object,
|
|
34
|
+
def generate(object, _options = {})
|
|
35
35
|
require 'psych'
|
|
36
36
|
Psych.dump(object)
|
|
37
37
|
end
|
|
38
38
|
|
|
39
|
-
def features
|
|
40
|
-
%w[parsing generation built-in]
|
|
41
|
-
end
|
|
42
|
-
|
|
43
39
|
private
|
|
44
40
|
|
|
45
41
|
def require_library(library_name)
|
|
@@ -10,6 +10,7 @@ require_relative 'serializers/xml/ox_serializer'
|
|
|
10
10
|
require_relative 'serializers/xml/nokogiri_serializer'
|
|
11
11
|
require_relative 'serializers/xml/oga_serializer'
|
|
12
12
|
require_relative 'serializers/xml/libxml_serializer'
|
|
13
|
+
require_relative 'serializers/xml/leptris_serializer'
|
|
13
14
|
|
|
14
15
|
# JSON Serializers
|
|
15
16
|
require_relative 'serializers/json/base_json_serializer'
|
|
@@ -38,7 +39,8 @@ module Serialbench
|
|
|
38
39
|
Xml::OxSerializer,
|
|
39
40
|
Xml::NokogiriSerializer,
|
|
40
41
|
Xml::OgaSerializer,
|
|
41
|
-
Xml::LibxmlSerializer
|
|
42
|
+
Xml::LibxmlSerializer,
|
|
43
|
+
Xml::LeptrisSerializer
|
|
42
44
|
],
|
|
43
45
|
json: [
|
|
44
46
|
Json::JsonSerializer,
|
|
@@ -80,11 +82,11 @@ module Serialbench
|
|
|
80
82
|
end
|
|
81
83
|
|
|
82
84
|
def self.available_for_format(format)
|
|
83
|
-
for_format(format).select
|
|
85
|
+
for_format(format).select(&:available?)
|
|
84
86
|
end
|
|
85
87
|
|
|
86
88
|
def self.available
|
|
87
|
-
all.select
|
|
89
|
+
all.select(&:available?)
|
|
88
90
|
end
|
|
89
91
|
end
|
|
90
92
|
end
|
|
@@ -30,22 +30,73 @@ module Serialbench
|
|
|
30
30
|
generator.generate_site
|
|
31
31
|
end
|
|
32
32
|
|
|
33
|
+
def self.resultset_payload(resultset)
|
|
34
|
+
new(output_path: '/dev/null', resultset: resultset).resultset_payload
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def self.export_raw_data(resultset, data_dir)
|
|
38
|
+
new(output_path: '/dev/null', resultset: resultset).export_raw_data_to(data_dir)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def resultset_payload
|
|
42
|
+
transform_resultset_for_dashboard(@resultset)
|
|
43
|
+
.merge('libraries' => libraries_information(@resultset))
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Static capability matrix (adapter declarations) plus the versions
|
|
47
|
+
# actually measured in this resultset.
|
|
48
|
+
def libraries_information(resultset)
|
|
49
|
+
versions = {}
|
|
50
|
+
resultset.results.each do |result|
|
|
51
|
+
serializers = result.benchmark_result.serializers || []
|
|
52
|
+
serializers.each { |si| versions[si.name] ||= si.version }
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
Serialbench::Serializers.all.filter_map do |adapter|
|
|
56
|
+
next if adapter.name == 'syck'
|
|
57
|
+
|
|
58
|
+
features = adapter.features
|
|
59
|
+
next if features.is_a?(Array)
|
|
60
|
+
|
|
61
|
+
{
|
|
62
|
+
'name' => adapter.name,
|
|
63
|
+
'format' => adapter.format.to_s,
|
|
64
|
+
'version' => versions[adapter.name] || 'unknown',
|
|
65
|
+
'features' => features.transform_values { |v| v == true }
|
|
66
|
+
}
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def export_raw_data_to(data_dir)
|
|
71
|
+
FileUtils.mkdir_p(data_dir)
|
|
72
|
+
export_resultset(@resultset, data_dir)
|
|
73
|
+
end
|
|
74
|
+
|
|
33
75
|
def generate_site
|
|
34
76
|
target_name = @result ? @result.environment_config.name : @resultset.name
|
|
35
|
-
data = @result ? @result.to_json : @resultset.to_json
|
|
36
77
|
|
|
37
78
|
puts "🏗️ Generating HTML site for #{@result ? 'run' : 'resultset'}: #{target_name}"
|
|
38
79
|
puts "Output: #{@output_path}"
|
|
39
80
|
|
|
81
|
+
# Transform data for dashboard.js compatibility
|
|
82
|
+
data = if @result
|
|
83
|
+
transform_result_for_dashboard(@result)
|
|
84
|
+
else
|
|
85
|
+
transform_resultset_for_dashboard(@resultset)
|
|
86
|
+
end
|
|
87
|
+
|
|
40
88
|
prepare_output_directory
|
|
41
89
|
render_site(
|
|
42
90
|
{
|
|
43
|
-
'data' => data,
|
|
91
|
+
'data' => JSON.generate(data),
|
|
44
92
|
'kind' => @result ? 'run' : 'resultset'
|
|
45
93
|
},
|
|
46
94
|
'format_based.liquid'
|
|
47
95
|
)
|
|
48
96
|
|
|
97
|
+
# Export raw data files for download
|
|
98
|
+
export_raw_data
|
|
99
|
+
|
|
49
100
|
puts "✅ Site generated successfully at: #{@output_path}"
|
|
50
101
|
@output_path
|
|
51
102
|
end
|
|
@@ -101,5 +152,186 @@ module Serialbench
|
|
|
101
152
|
|
|
102
153
|
FileUtils.cp_r(assets_source, assets_dest)
|
|
103
154
|
end
|
|
155
|
+
|
|
156
|
+
# Transform a single Result into dashboard-compatible format
|
|
157
|
+
# Dashboard expects: { combined_results: {...}, environments: {...}, metadata: {...} }
|
|
158
|
+
def transform_result_for_dashboard(result)
|
|
159
|
+
env_key = "env-#{result.platform.ruby_version}"
|
|
160
|
+
|
|
161
|
+
# Build combined_results structure
|
|
162
|
+
combined_results = build_combined_results(result, env_key)
|
|
163
|
+
|
|
164
|
+
# Build environments structure
|
|
165
|
+
environments = {
|
|
166
|
+
env_key => {
|
|
167
|
+
'ruby_version' => result.platform.ruby_version,
|
|
168
|
+
'ruby_platform' => result.platform.ruby_platform || "#{result.platform.os}-#{result.platform.arch}",
|
|
169
|
+
'os' => result.platform.os,
|
|
170
|
+
'arch' => result.platform.arch,
|
|
171
|
+
'source_file' => result.metadata.environment_config_path,
|
|
172
|
+
'timestamp' => result.metadata.created_at
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
{
|
|
177
|
+
'combined_results' => combined_results,
|
|
178
|
+
'environments' => environments,
|
|
179
|
+
'metadata' => {
|
|
180
|
+
'generated_at' => Time.now.iso8601
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
# Environment identity for the site: use the CI platform_string
|
|
186
|
+
# ("macos-26-ruby-3.4") when it is runner-shaped, so each runner label is
|
|
187
|
+
# its own environment; fall back to os-arch for other sources (docker,
|
|
188
|
+
# local runs, older resultsets).
|
|
189
|
+
def env_identity(result)
|
|
190
|
+
platform_string = result.platform.platform_string
|
|
191
|
+
ruby = result.platform.ruby_version
|
|
192
|
+
return platform_string if platform_string&.end_with?("-ruby-#{ruby}")
|
|
193
|
+
|
|
194
|
+
"#{result.platform.os}-#{result.platform.arch}-ruby-#{ruby}"
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
# Transform a ResultSet (collection of Results) into dashboard-compatible format
|
|
198
|
+
# Combines all results into a single dashboard structure
|
|
199
|
+
def transform_resultset_for_dashboard(resultset)
|
|
200
|
+
combined_results = {}
|
|
201
|
+
environments = {}
|
|
202
|
+
|
|
203
|
+
resultset.results.each do |result|
|
|
204
|
+
# Create unique env key for this result
|
|
205
|
+
env_key = env_identity(result)
|
|
206
|
+
|
|
207
|
+
# Merge this result's data into combined_results
|
|
208
|
+
result_combined = build_combined_results(result, env_key)
|
|
209
|
+
merge_combined_results!(combined_results, result_combined)
|
|
210
|
+
|
|
211
|
+
# Add environment info
|
|
212
|
+
environments[env_key] = {
|
|
213
|
+
'ruby_version' => result.platform.ruby_version,
|
|
214
|
+
'ruby_platform' => result.platform.ruby_platform || "#{result.platform.os}-#{result.platform.arch}",
|
|
215
|
+
'os' => result.platform.os,
|
|
216
|
+
'arch' => result.platform.arch,
|
|
217
|
+
'source_file' => result.metadata.environment_config_path,
|
|
218
|
+
'timestamp' => result.metadata.created_at
|
|
219
|
+
}
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
{
|
|
223
|
+
'combined_results' => combined_results,
|
|
224
|
+
'environments' => environments,
|
|
225
|
+
'metadata' => {
|
|
226
|
+
'resultset_name' => resultset.name,
|
|
227
|
+
'resultset_description' => resultset.description,
|
|
228
|
+
'total_runs' => resultset.results.size,
|
|
229
|
+
'generated_at' => Time.now.iso8601
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
# Deep merge results from multiple runs
|
|
235
|
+
def merge_combined_results!(target, source)
|
|
236
|
+
source.each do |operation, sizes|
|
|
237
|
+
target[operation] ||= {}
|
|
238
|
+
sizes.each do |size, formats|
|
|
239
|
+
target[operation][size] ||= {}
|
|
240
|
+
formats.each do |format, serializers|
|
|
241
|
+
target[operation][size][format] ||= {}
|
|
242
|
+
serializers.each do |serializer, envs|
|
|
243
|
+
target[operation][size][format][serializer] ||= {}
|
|
244
|
+
target[operation][size][format][serializer].merge!(envs)
|
|
245
|
+
end
|
|
246
|
+
end
|
|
247
|
+
end
|
|
248
|
+
end
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
def build_combined_results(result, env_key)
|
|
252
|
+
combined = {}
|
|
253
|
+
|
|
254
|
+
%w[parsing generation streaming].each do |operation|
|
|
255
|
+
combined[operation] = {}
|
|
256
|
+
|
|
257
|
+
operation_results = result.benchmark_result.send(operation)
|
|
258
|
+
next if operation_results.nil? || operation_results.empty?
|
|
259
|
+
|
|
260
|
+
operation_results.each do |perf|
|
|
261
|
+
size = perf.data_size
|
|
262
|
+
format = perf.format
|
|
263
|
+
serializer = perf.adapter
|
|
264
|
+
|
|
265
|
+
combined[operation][size] ||= {}
|
|
266
|
+
combined[operation][size][format] ||= {}
|
|
267
|
+
combined[operation][size][format][serializer] ||= {}
|
|
268
|
+
combined[operation][size][format][serializer][env_key] = {
|
|
269
|
+
'iterations_per_second' => perf.iterations_per_second,
|
|
270
|
+
'time_per_iteration' => perf.time_per_iteration
|
|
271
|
+
}
|
|
272
|
+
end
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
# Handle memory separately
|
|
276
|
+
if result.benchmark_result.memory && !result.benchmark_result.memory.empty?
|
|
277
|
+
combined['memory'] = {}
|
|
278
|
+
|
|
279
|
+
result.benchmark_result.memory.each do |mem|
|
|
280
|
+
size = mem.data_size
|
|
281
|
+
format = mem.format
|
|
282
|
+
serializer = mem.adapter
|
|
283
|
+
|
|
284
|
+
combined['memory'][size] ||= {}
|
|
285
|
+
combined['memory'][size][format] ||= {}
|
|
286
|
+
combined['memory'][size][format][serializer] ||= {}
|
|
287
|
+
combined['memory'][size][format][serializer][env_key] = {
|
|
288
|
+
'allocated_memory' => mem.allocated_memory,
|
|
289
|
+
'retained_memory' => mem.retained_memory
|
|
290
|
+
}
|
|
291
|
+
end
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
combined
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
# Export raw data files for download
|
|
298
|
+
def export_raw_data
|
|
299
|
+
data_dir = File.join(@output_path, 'data')
|
|
300
|
+
FileUtils.mkdir_p(data_dir)
|
|
301
|
+
|
|
302
|
+
if @result
|
|
303
|
+
# Export single result
|
|
304
|
+
export_single_result(@result, data_dir)
|
|
305
|
+
elsif @resultset
|
|
306
|
+
# Export all results in resultset plus the complete resultset
|
|
307
|
+
export_resultset(@resultset, data_dir)
|
|
308
|
+
end
|
|
309
|
+
|
|
310
|
+
puts "📦 Raw data files exported to: #{data_dir}"
|
|
311
|
+
end
|
|
312
|
+
|
|
313
|
+
def export_single_result(result, data_dir)
|
|
314
|
+
# Create filename based on platform info
|
|
315
|
+
env_key = env_identity(result)
|
|
316
|
+
filename = "#{env_key}.yaml"
|
|
317
|
+
filepath = File.join(data_dir, filename)
|
|
318
|
+
|
|
319
|
+
# Write result as YAML
|
|
320
|
+
File.write(filepath, result.to_yaml)
|
|
321
|
+
puts " 📄 Exported: #{filename}"
|
|
322
|
+
end
|
|
323
|
+
|
|
324
|
+
def export_resultset(resultset, data_dir)
|
|
325
|
+
# Export each individual result
|
|
326
|
+
resultset.results.each do |result|
|
|
327
|
+
export_single_result(result, data_dir)
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
# Export complete resultset
|
|
331
|
+
resultset_filename = 'resultset.yaml'
|
|
332
|
+
resultset_filepath = File.join(data_dir, resultset_filename)
|
|
333
|
+
File.write(resultset_filepath, resultset.to_yaml)
|
|
334
|
+
puts " 📄 Exported: #{resultset_filename} (complete dataset)"
|
|
335
|
+
end
|
|
104
336
|
end
|
|
105
337
|
end
|
|
@@ -13,59 +13,7 @@
|
|
|
13
13
|
flex-wrap: wrap;
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
padding: 12px 24px;
|
|
18
|
-
border: none;
|
|
19
|
-
background: var(--card-bg);
|
|
20
|
-
color: var(--text-color);
|
|
21
|
-
border-radius: 25px;
|
|
22
|
-
cursor: pointer;
|
|
23
|
-
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
24
|
-
font-weight: 600;
|
|
25
|
-
font-size: 0.95em;
|
|
26
|
-
box-shadow: var(--shadow-soft);
|
|
27
|
-
border: 2px solid transparent;
|
|
28
|
-
position: relative;
|
|
29
|
-
overflow: hidden;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
.format-tab::before {
|
|
33
|
-
content: '';
|
|
34
|
-
position: absolute;
|
|
35
|
-
top: 0;
|
|
36
|
-
left: -100%;
|
|
37
|
-
width: 100%;
|
|
38
|
-
height: 100%;
|
|
39
|
-
background: var(--gradient-accent);
|
|
40
|
-
transition: left 0.3s ease;
|
|
41
|
-
z-index: 0;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
.format-tab:hover {
|
|
45
|
-
transform: translateY(-3px);
|
|
46
|
-
box-shadow: var(--shadow-medium);
|
|
47
|
-
border-color: var(--secondary-color);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
.format-tab:hover::before {
|
|
51
|
-
left: 0;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
.format-tab > * {
|
|
55
|
-
position: relative;
|
|
56
|
-
z-index: 1;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
.format-tab.active {
|
|
60
|
-
background: var(--gradient-primary);
|
|
61
|
-
color: white;
|
|
62
|
-
transform: translateY(-2px);
|
|
63
|
-
box-shadow: var(--shadow-medium);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
.format-tab.active::before {
|
|
67
|
-
display: none;
|
|
68
|
-
}
|
|
16
|
+
/* Format tab styles are defined in themes.css */
|
|
69
17
|
|
|
70
18
|
/* Format Content */
|
|
71
19
|
.format-content {
|
|
@@ -259,7 +259,7 @@ body {
|
|
|
259
259
|
|
|
260
260
|
.format-tab {
|
|
261
261
|
padding: var(--space-sm) var(--space-lg);
|
|
262
|
-
background:
|
|
262
|
+
background: var(--bg-card);
|
|
263
263
|
border: 1px solid var(--border-primary);
|
|
264
264
|
border-radius: var(--radius-md);
|
|
265
265
|
color: var(--text-secondary);
|
|
@@ -271,14 +271,15 @@ body {
|
|
|
271
271
|
letter-spacing: 0.05em;
|
|
272
272
|
}
|
|
273
273
|
|
|
274
|
-
.format-tab:hover {
|
|
275
|
-
background: var(--bg-
|
|
274
|
+
.format-tab:hover:not(.active) {
|
|
275
|
+
background: var(--bg-tertiary);
|
|
276
276
|
color: var(--text-primary);
|
|
277
|
+
border-color: var(--border-secondary);
|
|
277
278
|
transform: translateY(-1px);
|
|
278
279
|
}
|
|
279
280
|
|
|
280
281
|
.format-tab.active {
|
|
281
|
-
background: var(--
|
|
282
|
+
background: var(--accent-primary);
|
|
282
283
|
border-color: var(--accent-primary);
|
|
283
284
|
color: white;
|
|
284
285
|
box-shadow: var(--shadow-md);
|
|
@@ -172,9 +172,15 @@ function createPerformanceChart(canvasId, title, data, metric, environments) {
|
|
|
172
172
|
console.log(`🌍 Environments:`, environments);
|
|
173
173
|
console.log(`📊 Raw data:`, data);
|
|
174
174
|
|
|
175
|
+
// Get theme-aware colors
|
|
176
|
+
const isDarkMode = document.documentElement.getAttribute('data-theme') === 'dark';
|
|
177
|
+
const textColor = getComputedStyle(document.documentElement).getPropertyValue('--text-primary').trim() || (isDarkMode ? '#F8FAFC' : '#0F172A');
|
|
178
|
+
const gridColor = getComputedStyle(document.documentElement).getPropertyValue('--border-primary').trim() || (isDarkMode ? '#475569' : '#E2E8F0');
|
|
179
|
+
|
|
180
|
+
// High contrast color palette
|
|
175
181
|
const colors = [
|
|
176
|
-
'#
|
|
177
|
-
'#
|
|
182
|
+
'#F97316', '#3B82F6', '#10B981', '#EF4444', '#8B5CF6',
|
|
183
|
+
'#F59E0B', '#EC4899', '#06B6D4', '#84CC16', '#6366F1'
|
|
178
184
|
];
|
|
179
185
|
|
|
180
186
|
const datasets = serializers.map((serializer, index) => {
|
|
@@ -224,14 +230,15 @@ function createPerformanceChart(canvasId, title, data, metric, environments) {
|
|
|
224
230
|
display: true,
|
|
225
231
|
text: title,
|
|
226
232
|
font: { size: 16, weight: 'bold' },
|
|
227
|
-
color:
|
|
233
|
+
color: textColor
|
|
228
234
|
},
|
|
229
235
|
legend: {
|
|
230
236
|
position: 'top',
|
|
231
237
|
labels: {
|
|
232
238
|
usePointStyle: true,
|
|
233
239
|
padding: 20,
|
|
234
|
-
font: { size: 12 }
|
|
240
|
+
font: { size: 12 },
|
|
241
|
+
color: textColor
|
|
235
242
|
}
|
|
236
243
|
}
|
|
237
244
|
},
|
|
@@ -241,17 +248,25 @@ function createPerformanceChart(canvasId, title, data, metric, environments) {
|
|
|
241
248
|
title: {
|
|
242
249
|
display: true,
|
|
243
250
|
text: 'Iterations per Second',
|
|
244
|
-
font: { size: 12, weight: 'bold' }
|
|
251
|
+
font: { size: 12, weight: 'bold' },
|
|
252
|
+
color: textColor
|
|
253
|
+
},
|
|
254
|
+
ticks: {
|
|
255
|
+
color: textColor
|
|
245
256
|
},
|
|
246
257
|
grid: {
|
|
247
|
-
color:
|
|
258
|
+
color: gridColor
|
|
248
259
|
}
|
|
249
260
|
},
|
|
250
261
|
x: {
|
|
251
262
|
title: {
|
|
252
263
|
display: true,
|
|
253
264
|
text: 'Ruby Versions',
|
|
254
|
-
font: { size: 12, weight: 'bold' }
|
|
265
|
+
font: { size: 12, weight: 'bold' },
|
|
266
|
+
color: textColor
|
|
267
|
+
},
|
|
268
|
+
ticks: {
|
|
269
|
+
color: textColor
|
|
255
270
|
},
|
|
256
271
|
grid: {
|
|
257
272
|
display: false
|
|
@@ -294,9 +309,15 @@ function createMemoryChart(canvasId, title, data, environments) {
|
|
|
294
309
|
return;
|
|
295
310
|
}
|
|
296
311
|
|
|
312
|
+
// Get theme-aware colors
|
|
313
|
+
const isDarkMode = document.documentElement.getAttribute('data-theme') === 'dark';
|
|
314
|
+
const textColor = getComputedStyle(document.documentElement).getPropertyValue('--text-primary').trim() || (isDarkMode ? '#F8FAFC' : '#0F172A');
|
|
315
|
+
const gridColor = getComputedStyle(document.documentElement).getPropertyValue('--border-primary').trim() || (isDarkMode ? '#475569' : '#E2E8F0');
|
|
316
|
+
|
|
317
|
+
// High contrast color palette
|
|
297
318
|
const colors = [
|
|
298
|
-
'#
|
|
299
|
-
'#
|
|
319
|
+
'#F97316', '#3B82F6', '#10B981', '#EF4444', '#8B5CF6',
|
|
320
|
+
'#F59E0B', '#EC4899', '#06B6D4', '#84CC16', '#6366F1'
|
|
300
321
|
];
|
|
301
322
|
|
|
302
323
|
const datasets = serializers.map((serializer, index) => {
|
|
@@ -338,14 +359,15 @@ function createMemoryChart(canvasId, title, data, environments) {
|
|
|
338
359
|
display: true,
|
|
339
360
|
text: title,
|
|
340
361
|
font: { size: 16, weight: 'bold' },
|
|
341
|
-
color:
|
|
362
|
+
color: textColor
|
|
342
363
|
},
|
|
343
364
|
legend: {
|
|
344
365
|
position: 'top',
|
|
345
366
|
labels: {
|
|
346
367
|
usePointStyle: true,
|
|
347
368
|
padding: 20,
|
|
348
|
-
font: { size: 12 }
|
|
369
|
+
font: { size: 12 },
|
|
370
|
+
color: textColor
|
|
349
371
|
}
|
|
350
372
|
}
|
|
351
373
|
},
|
|
@@ -355,17 +377,25 @@ function createMemoryChart(canvasId, title, data, environments) {
|
|
|
355
377
|
title: {
|
|
356
378
|
display: true,
|
|
357
379
|
text: 'Memory Usage (MB)',
|
|
358
|
-
font: { size: 12, weight: 'bold' }
|
|
380
|
+
font: { size: 12, weight: 'bold' },
|
|
381
|
+
color: textColor
|
|
382
|
+
},
|
|
383
|
+
ticks: {
|
|
384
|
+
color: textColor
|
|
359
385
|
},
|
|
360
386
|
grid: {
|
|
361
|
-
color:
|
|
387
|
+
color: gridColor
|
|
362
388
|
}
|
|
363
389
|
},
|
|
364
390
|
x: {
|
|
365
391
|
title: {
|
|
366
392
|
display: true,
|
|
367
393
|
text: 'Ruby Versions',
|
|
368
|
-
font: { size: 12, weight: 'bold' }
|
|
394
|
+
font: { size: 12, weight: 'bold' },
|
|
395
|
+
color: textColor
|
|
396
|
+
},
|
|
397
|
+
ticks: {
|
|
398
|
+
color: textColor
|
|
369
399
|
},
|
|
370
400
|
grid: {
|
|
371
401
|
display: false
|
|
@@ -108,7 +108,9 @@ class SerialBenchDashboard {
|
|
|
108
108
|
|
|
109
109
|
if (this.data.environments) {
|
|
110
110
|
Object.values(this.data.environments).forEach(env => {
|
|
111
|
-
|
|
111
|
+
// Use os-arch combination instead of ruby_platform
|
|
112
|
+
const platformKey = `${env.os}-${env.arch}`;
|
|
113
|
+
platforms.add(platformKey);
|
|
112
114
|
rubyVersions.add(env.ruby_version);
|
|
113
115
|
});
|
|
114
116
|
}
|
|
@@ -120,7 +122,7 @@ class SerialBenchDashboard {
|
|
|
120
122
|
Array.from(platforms).sort().forEach(platform => {
|
|
121
123
|
const option = document.createElement('option');
|
|
122
124
|
option.value = platform;
|
|
123
|
-
option.textContent =
|
|
125
|
+
option.textContent = platform;
|
|
124
126
|
platformSelect.appendChild(option);
|
|
125
127
|
});
|
|
126
128
|
}
|
|
@@ -148,15 +150,6 @@ class SerialBenchDashboard {
|
|
|
148
150
|
}
|
|
149
151
|
}
|
|
150
152
|
|
|
151
|
-
formatPlatformName(platform) {
|
|
152
|
-
const platformMap = {
|
|
153
|
-
'aarch64-linux': 'ARM64 Linux',
|
|
154
|
-
'x86_64-linux': 'x64 Linux',
|
|
155
|
-
'x86_64-darwin': 'x64 macOS',
|
|
156
|
-
'aarch64-darwin': 'ARM64 macOS'
|
|
157
|
-
};
|
|
158
|
-
return platformMap[platform] || platform;
|
|
159
|
-
}
|
|
160
153
|
|
|
161
154
|
setupFilterEventListeners() {
|
|
162
155
|
// Platform filter
|
|
@@ -213,7 +206,9 @@ class SerialBenchDashboard {
|
|
|
213
206
|
// Set all platforms and versions by default
|
|
214
207
|
if (this.data.environments) {
|
|
215
208
|
Object.values(this.data.environments).forEach(env => {
|
|
216
|
-
|
|
209
|
+
// Use os-arch combination instead of ruby_platform
|
|
210
|
+
const platformKey = `${env.os}-${env.arch}`;
|
|
211
|
+
this.filters.platforms.add(platformKey);
|
|
217
212
|
this.filters.rubyVersions.add(env.ruby_version);
|
|
218
213
|
});
|
|
219
214
|
}
|
|
@@ -336,8 +331,10 @@ class SerialBenchDashboard {
|
|
|
336
331
|
shouldIncludeEnvironment(env) {
|
|
337
332
|
if (!env) return false;
|
|
338
333
|
|
|
334
|
+
// Use os-arch combination for platform matching
|
|
335
|
+
const platformKey = `${env.os}-${env.arch}`;
|
|
339
336
|
const platformMatch = this.filters.platforms.size === 0 ||
|
|
340
|
-
this.filters.platforms.has(
|
|
337
|
+
this.filters.platforms.has(platformKey);
|
|
341
338
|
const versionMatch = this.filters.rubyVersions.size === 0 ||
|
|
342
339
|
this.filters.rubyVersions.has(env.ruby_version);
|
|
343
340
|
|
|
@@ -634,8 +631,10 @@ class SerialBenchDashboard {
|
|
|
634
631
|
// Show all environments, but apply platform and version filters only (not format)
|
|
635
632
|
const filteredEnvs = Object.entries(this.data.environments)
|
|
636
633
|
.filter(([key, env]) => {
|
|
634
|
+
// Use os-arch combination for platform matching
|
|
635
|
+
const platformKey = `${env.os}-${env.arch}`;
|
|
637
636
|
const platformMatch = this.filters.platforms.size === 0 ||
|
|
638
|
-
this.filters.platforms.has(
|
|
637
|
+
this.filters.platforms.has(platformKey);
|
|
639
638
|
const versionMatch = this.filters.rubyVersions.size === 0 ||
|
|
640
639
|
this.filters.rubyVersions.has(env.ruby_version);
|
|
641
640
|
return platformMatch && versionMatch;
|
|
@@ -649,7 +648,7 @@ class SerialBenchDashboard {
|
|
|
649
648
|
container.innerHTML = filteredEnvs.map(([key, env]) => `
|
|
650
649
|
<div class="environment-card fade-in-up">
|
|
651
650
|
<h3 class="environment-card-title">
|
|
652
|
-
Ruby ${env.ruby_version} on ${
|
|
651
|
+
Ruby ${env.ruby_version} on ${env.os}-${env.arch}
|
|
653
652
|
</h3>
|
|
654
653
|
<p><strong>Source:</strong> ${env.source_file ? env.source_file.split('/').pop() : 'Unknown'}</p>
|
|
655
654
|
<p><strong>Timestamp:</strong> ${new Date(env.timestamp).toLocaleString()}</p>
|