serialbench 0.2.0 → 0.4.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/scripts/push-to-data.sh +65 -0
- data/.github/workflows/benchmark.yml +98 -172
- data/.github/workflows/release.yml +68 -13
- data/Gemfile +1 -1
- data/config/benchmarks/full-json.yml +25 -0
- data/config/benchmarks/full-toml.yml +25 -0
- data/config/benchmarks/full-xml.yml +26 -0
- data/config/benchmarks/full-yaml.yml +25 -0
- data/config/benchmarks/full.yml +9 -4
- data/lib/serialbench/benchmark_runner.rb +32 -320
- data/lib/serialbench/cli/benchmark_cli.rb +1 -1
- data/lib/serialbench/cli.rb +0 -12
- data/lib/serialbench/models/benchmark_result.rb +2 -0
- data/lib/serialbench/models/result_store.rb +0 -38
- data/lib/serialbench/models.rb +0 -5
- data/lib/serialbench/serializers/base_serializer.rb +11 -3
- data/lib/serialbench/serializers/json/base_json_serializer.rb +8 -20
- data/lib/serialbench/serializers/json/json_serializer.rb +0 -16
- data/lib/serialbench/serializers/json/oj_serializer.rb +2 -17
- data/lib/serialbench/serializers/json/yajl_serializer.rb +2 -2
- data/lib/serialbench/serializers/toml/base_toml_serializer.rb +8 -29
- data/lib/serialbench/serializers/toml/toml_rb_serializer.rb +0 -16
- data/lib/serialbench/serializers/toml/tomlib_serializer.rb +0 -4
- data/lib/serialbench/serializers/toml/tomlrb_serializer.rb +2 -18
- data/lib/serialbench/serializers/xml/base_xml_serializer.rb +10 -26
- data/lib/serialbench/serializers/xml/leptris_serializer.rb +13 -10
- data/lib/serialbench/serializers/xml/libxml_serializer.rb +4 -9
- data/lib/serialbench/serializers/xml/nokogiri_serializer.rb +4 -9
- data/lib/serialbench/serializers/xml/oga_serializer.rb +6 -4
- data/lib/serialbench/serializers/xml/ox_serializer.rb +2 -2
- data/lib/serialbench/serializers/xml/rexml_serializer.rb +5 -12
- data/lib/serialbench/serializers/yaml/base_yaml_serializer.rb +1 -9
- data/lib/serialbench/serializers/yaml/syck_serializer.rb +0 -8
- data/lib/serialbench/test_data.rb +315 -0
- data/lib/serialbench/version.rb +1 -1
- data/lib/serialbench.rb +2 -1
- data/serialbench.gemspec +1 -2
- metadata +13 -59
- data/lib/serialbench/cli/resultset_cli.rb +0 -301
- data/lib/serialbench/models/result_set.rb +0 -79
- data/lib/serialbench/site_generator.rb +0 -337
- data/lib/serialbench/templates/assets/css/benchmark_report.css +0 -535
- data/lib/serialbench/templates/assets/css/format_based.css +0 -474
- data/lib/serialbench/templates/assets/css/themes.css +0 -589
- data/lib/serialbench/templates/assets/js/chart_helpers.js +0 -411
- data/lib/serialbench/templates/assets/js/dashboard.js +0 -795
- data/lib/serialbench/templates/assets/js/navigation.js +0 -142
- data/lib/serialbench/templates/base.liquid +0 -49
- data/lib/serialbench/templates/format_based.liquid +0 -507
- data/lib/serialbench/templates/partials/chart_section.liquid +0 -4
- data/site/.gitignore +0 -5
- data/site/README.md +0 -32
- data/site/astro.config.mjs +0 -12
- data/site/package-lock.json +0 -6160
- data/site/package.json +0 -22
- data/site/public/favicon.svg +0 -4
- data/site/scripts/gen-sample-data.mjs +0 -154
- data/site/src/components/AvailabilityMatrix.astro +0 -61
- data/site/src/components/CalibratedLeaderboard.vue +0 -134
- data/site/src/components/CitationBox.vue +0 -50
- data/site/src/components/DashboardConsole.vue +0 -193
- data/site/src/components/FeaturesTable.astro +0 -97
- data/site/src/components/MemoryPanel.vue +0 -44
- data/site/src/components/OpsChart.vue +0 -135
- data/site/src/config.ts +0 -18
- data/site/src/layouts/Layout.astro +0 -85
- data/site/src/lib/channels.ts +0 -24
- data/site/src/lib/dashboard.ts +0 -269
- data/site/src/pages/index.astro +0 -44
- data/site/src/pages/library/[slug].astro +0 -128
- data/site/src/pages/methodology.astro +0 -55
- data/site/src/styles/global.css +0 -103
- data/site/tsconfig.json +0 -5
data/config/benchmarks/full.yml
CHANGED
|
@@ -15,15 +15,20 @@ formats:
|
|
|
15
15
|
- yaml
|
|
16
16
|
- toml
|
|
17
17
|
|
|
18
|
+
# Trimmed from 20/5/2 (warmup 3): a healthy leg took ~70 minutes at that
|
|
19
|
+
# setting, which both inflates weekly cost and leaves legs exposed to
|
|
20
|
+
# runner-eviction flakiness. 10/3/1 with warmup 2 halves runtime with
|
|
21
|
+
# acceptable added noise.
|
|
18
22
|
iterations:
|
|
19
|
-
small:
|
|
20
|
-
medium:
|
|
21
|
-
large:
|
|
23
|
+
small: 10
|
|
24
|
+
medium: 3
|
|
25
|
+
large: 1
|
|
22
26
|
|
|
23
27
|
operations:
|
|
24
28
|
- parse
|
|
25
29
|
- generate
|
|
26
30
|
- streaming
|
|
31
|
+
- xpath
|
|
27
32
|
- memory
|
|
28
33
|
|
|
29
|
-
warmup:
|
|
34
|
+
warmup: 2
|
|
@@ -24,6 +24,19 @@ module Serialbench
|
|
|
24
24
|
load_test_data
|
|
25
25
|
end
|
|
26
26
|
|
|
27
|
+
# Each operation maps to a lambda. Adding an operation = one entry.
|
|
28
|
+
OPERATIONS = {
|
|
29
|
+
'parsing' => ->(s, data) { s.parse(data) },
|
|
30
|
+
'generation' => ->(s, data) { s.generate(s.parse(data)) },
|
|
31
|
+
'xpath' => lambda { |s, data|
|
|
32
|
+
doc = s.parse(data)
|
|
33
|
+
s.xpath_query(doc, '//book')
|
|
34
|
+
s.xpath_query(doc, "//book[@id='101']")
|
|
35
|
+
s.xpath_query(doc, '//book[price > 30]/title')
|
|
36
|
+
},
|
|
37
|
+
'streaming' => ->(s, data) { s.stream_parse(data) { |_event, _data| } },
|
|
38
|
+
}.freeze
|
|
39
|
+
|
|
27
40
|
def run_all_benchmarks
|
|
28
41
|
puts 'Serialbench - Running comprehensive serialization performance tests'
|
|
29
42
|
puts '=' * 70
|
|
@@ -32,34 +45,18 @@ module Serialbench
|
|
|
32
45
|
puts "Test data sizes: #{@test_data.keys.join(', ')}"
|
|
33
46
|
puts
|
|
34
47
|
|
|
48
|
+
results = {}
|
|
49
|
+
OPERATIONS.each do |name, handler|
|
|
50
|
+
results[name.to_sym] = run_benchmark_type(name, name, &handler)
|
|
51
|
+
end
|
|
52
|
+
results[:memory] = run_memory_benchmarks
|
|
53
|
+
|
|
35
54
|
Models::BenchmarkResult.new(
|
|
36
55
|
serializers: Serializers.information,
|
|
37
|
-
|
|
38
|
-
generation: run_generation_benchmarks,
|
|
39
|
-
memory: run_memory_benchmarks,
|
|
40
|
-
streaming: run_streaming_benchmarks
|
|
56
|
+
**results
|
|
41
57
|
)
|
|
42
58
|
end
|
|
43
59
|
|
|
44
|
-
def run_parsing_benchmarks
|
|
45
|
-
run_benchmark_type('parsing', 'parse') do |serializer, data|
|
|
46
|
-
serializer.parse(data)
|
|
47
|
-
end
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
def run_generation_benchmarks
|
|
51
|
-
run_benchmark_type('generation', 'generation') do |serializer, data|
|
|
52
|
-
document = serializer.parse(data)
|
|
53
|
-
serializer.generate(document)
|
|
54
|
-
end
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
def run_streaming_benchmarks
|
|
58
|
-
run_benchmark_type('streaming', 'stream parse') do |serializer, data|
|
|
59
|
-
serializer.stream_parse(data) { |event, data| }
|
|
60
|
-
end
|
|
61
|
-
end
|
|
62
|
-
|
|
63
60
|
def run_memory_benchmarks
|
|
64
61
|
puts "\nRunning memory usage benchmarks..."
|
|
65
62
|
return [] unless defined?(::MemoryProfiler)
|
|
@@ -148,313 +145,28 @@ module Serialbench
|
|
|
148
145
|
|
|
149
146
|
case type_name
|
|
150
147
|
when 'generation'
|
|
151
|
-
serializers.select(
|
|
148
|
+
serializers.select { |s| s.supports?(:generate) }
|
|
152
149
|
when 'streaming'
|
|
153
|
-
serializers.select(
|
|
150
|
+
serializers.select { |s| s.supports?(:sax) || s.supports?(:streaming) }
|
|
151
|
+
when 'xpath'
|
|
152
|
+
serializers.select { |s| s.supports?(:xpath) }
|
|
154
153
|
else
|
|
155
154
|
serializers
|
|
156
155
|
end
|
|
157
156
|
end
|
|
158
157
|
|
|
159
158
|
def get_iterations_for_size(size)
|
|
160
|
-
@benchmark_config.iterations
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
# Initialize test data structure
|
|
168
|
-
@test_data = {}
|
|
169
|
-
data_sizes.each { |size| @test_data[size] = {} }
|
|
170
|
-
|
|
171
|
-
# Generate data for each format and size
|
|
172
|
-
@benchmark_config.formats.each do |format|
|
|
173
|
-
data_sizes.each do |size|
|
|
174
|
-
@test_data[size][format] = generate_test_data(format, size)
|
|
175
|
-
end
|
|
176
|
-
end
|
|
177
|
-
|
|
178
|
-
# Try to load real test files if they exist
|
|
179
|
-
data_sizes.each do |size|
|
|
180
|
-
@benchmark_config.formats.each do |format|
|
|
181
|
-
file_path = "test_data/#{size}.#{format}"
|
|
182
|
-
@test_data[size][format] = File.read(file_path) if File.exist?(file_path)
|
|
183
|
-
end
|
|
159
|
+
iterations = @benchmark_config.iterations
|
|
160
|
+
case size.to_s
|
|
161
|
+
when 'small' then iterations.small
|
|
162
|
+
when 'medium' then iterations.medium
|
|
163
|
+
when 'large' then iterations.large
|
|
164
|
+
else raise ArgumentError, "no iteration count configured for #{size}"
|
|
184
165
|
end
|
|
185
166
|
end
|
|
186
167
|
|
|
187
|
-
def
|
|
188
|
-
|
|
189
|
-
send(method_name)
|
|
190
|
-
end
|
|
191
|
-
|
|
192
|
-
# Shared data structure generators
|
|
193
|
-
def small_test_data_structure
|
|
194
|
-
{
|
|
195
|
-
config: {
|
|
196
|
-
database: {
|
|
197
|
-
host: 'localhost',
|
|
198
|
-
port: 5432,
|
|
199
|
-
name: 'myapp',
|
|
200
|
-
user: 'admin',
|
|
201
|
-
password: 'secret'
|
|
202
|
-
},
|
|
203
|
-
cache: {
|
|
204
|
-
enabled: true,
|
|
205
|
-
ttl: 3600
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
end
|
|
210
|
-
|
|
211
|
-
def medium_test_data_structure
|
|
212
|
-
{
|
|
213
|
-
users: (1..1000).map do |i|
|
|
214
|
-
{
|
|
215
|
-
id: i,
|
|
216
|
-
name: "User #{i}",
|
|
217
|
-
email: "user#{i}@example.com",
|
|
218
|
-
created_at: "2023-01-#{(i % 28) + 1}T10:00:00Z",
|
|
219
|
-
profile: {
|
|
220
|
-
age: 20 + (i % 50),
|
|
221
|
-
city: "City #{i % 100}",
|
|
222
|
-
preferences: {
|
|
223
|
-
theme: i.even? ? 'dark' : 'light',
|
|
224
|
-
notifications: (i % 3).zero?
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
end
|
|
229
|
-
}
|
|
230
|
-
end
|
|
231
|
-
|
|
232
|
-
def large_test_data_structure
|
|
233
|
-
{
|
|
234
|
-
dataset: {
|
|
235
|
-
header: {
|
|
236
|
-
created: '2023-01-01T00:00:00Z',
|
|
237
|
-
count: 10_000,
|
|
238
|
-
format: 'data'
|
|
239
|
-
},
|
|
240
|
-
records: (1..10_000).map do |i|
|
|
241
|
-
{
|
|
242
|
-
id: i,
|
|
243
|
-
timestamp: "2023-01-01T#{format('%02d', i % 24)}:#{format('%02d', i % 60)}:#{format('%02d', i % 60)}Z",
|
|
244
|
-
data: {
|
|
245
|
-
field1: "Value #{i}",
|
|
246
|
-
field2: i * 2,
|
|
247
|
-
field3: (i % 100).zero? ? 'special' : 'normal',
|
|
248
|
-
nested: [
|
|
249
|
-
"Item #{i}-1",
|
|
250
|
-
"Item #{i}-2",
|
|
251
|
-
"Item #{i}-3"
|
|
252
|
-
]
|
|
253
|
-
},
|
|
254
|
-
metadata: {
|
|
255
|
-
source: 'generator',
|
|
256
|
-
version: '1.0',
|
|
257
|
-
checksum: i.to_s(16)
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
end
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
end
|
|
264
|
-
|
|
265
|
-
# XML test data generators
|
|
266
|
-
def generate_small_xml
|
|
267
|
-
data = small_test_data_structure
|
|
268
|
-
<<~XML
|
|
269
|
-
<?xml version="1.0" encoding="UTF-8"?>
|
|
270
|
-
<config>
|
|
271
|
-
<database>
|
|
272
|
-
<host>#{data[:config][:database][:host]}</host>
|
|
273
|
-
<port>#{data[:config][:database][:port]}</port>
|
|
274
|
-
<name>#{data[:config][:database][:name]}</name>
|
|
275
|
-
<user>#{data[:config][:database][:user]}</user>
|
|
276
|
-
<password>#{data[:config][:database][:password]}</password>
|
|
277
|
-
</database>
|
|
278
|
-
<cache>
|
|
279
|
-
<enabled>#{data[:config][:cache][:enabled]}</enabled>
|
|
280
|
-
<ttl>#{data[:config][:cache][:ttl]}</ttl>
|
|
281
|
-
</cache>
|
|
282
|
-
</config>
|
|
283
|
-
XML
|
|
284
|
-
end
|
|
285
|
-
|
|
286
|
-
def generate_medium_xml
|
|
287
|
-
data = medium_test_data_structure
|
|
288
|
-
users = data[:users].map do |user|
|
|
289
|
-
<<~USER
|
|
290
|
-
<user id="#{user[:id]}">
|
|
291
|
-
<name>#{user[:name]}</name>
|
|
292
|
-
<email>#{user[:email]}</email>
|
|
293
|
-
<created_at>#{user[:created_at]}</created_at>
|
|
294
|
-
<profile>
|
|
295
|
-
<age>#{user[:profile][:age]}</age>
|
|
296
|
-
<city>#{user[:profile][:city]}</city>
|
|
297
|
-
<preferences>
|
|
298
|
-
<theme>#{user[:profile][:preferences][:theme]}</theme>
|
|
299
|
-
<notifications>#{user[:profile][:preferences][:notifications]}</notifications>
|
|
300
|
-
</preferences>
|
|
301
|
-
</profile>
|
|
302
|
-
</user>
|
|
303
|
-
USER
|
|
304
|
-
end.join
|
|
305
|
-
|
|
306
|
-
<<~XML
|
|
307
|
-
<?xml version="1.0" encoding="UTF-8"?>
|
|
308
|
-
<users>
|
|
309
|
-
#{users}
|
|
310
|
-
</users>
|
|
311
|
-
XML
|
|
312
|
-
end
|
|
313
|
-
|
|
314
|
-
def generate_large_xml
|
|
315
|
-
data = large_test_data_structure
|
|
316
|
-
records = data[:dataset][:records].map do |record|
|
|
317
|
-
nested_items = record[:data][:nested].map { |item| " <item>#{item}</item>" }.join("\n")
|
|
318
|
-
<<~RECORD
|
|
319
|
-
<record id="#{record[:id]}">
|
|
320
|
-
<timestamp>#{record[:timestamp]}</timestamp>
|
|
321
|
-
<data>
|
|
322
|
-
<field1>#{record[:data][:field1]}</field1>
|
|
323
|
-
<field2>#{record[:data][:field2]}</field2>
|
|
324
|
-
<field3>#{record[:data][:field3]}</field3>
|
|
325
|
-
<nested>
|
|
326
|
-
#{nested_items}
|
|
327
|
-
</nested>
|
|
328
|
-
</data>
|
|
329
|
-
<metadata>
|
|
330
|
-
<source>#{record[:metadata][:source]}</source>
|
|
331
|
-
<version>#{record[:metadata][:version]}</version>
|
|
332
|
-
<checksum>#{record[:metadata][:checksum]}</checksum>
|
|
333
|
-
</metadata>
|
|
334
|
-
</record>
|
|
335
|
-
RECORD
|
|
336
|
-
end.join
|
|
337
|
-
|
|
338
|
-
<<~XML
|
|
339
|
-
<?xml version="1.0" encoding="UTF-8"?>
|
|
340
|
-
<dataset>
|
|
341
|
-
<header>
|
|
342
|
-
<created>#{data[:dataset][:header][:created]}</created>
|
|
343
|
-
<count>#{data[:dataset][:header][:count]}</count>
|
|
344
|
-
<format>xml</format>
|
|
345
|
-
</header>
|
|
346
|
-
<records>
|
|
347
|
-
#{records}
|
|
348
|
-
</records>
|
|
349
|
-
</dataset>
|
|
350
|
-
XML
|
|
351
|
-
end
|
|
352
|
-
|
|
353
|
-
# JSON test data generators
|
|
354
|
-
def generate_small_json
|
|
355
|
-
JSON.generate(small_test_data_structure)
|
|
356
|
-
end
|
|
357
|
-
|
|
358
|
-
def generate_medium_json
|
|
359
|
-
JSON.generate(medium_test_data_structure)
|
|
360
|
-
end
|
|
361
|
-
|
|
362
|
-
def generate_large_json
|
|
363
|
-
data = large_test_data_structure
|
|
364
|
-
data[:dataset][:header][:format] = 'json'
|
|
365
|
-
JSON.generate(data)
|
|
366
|
-
end
|
|
367
|
-
|
|
368
|
-
# YAML test data generators
|
|
369
|
-
def generate_small_yaml
|
|
370
|
-
small_test_data_structure.to_yaml
|
|
371
|
-
end
|
|
372
|
-
|
|
373
|
-
def generate_medium_yaml
|
|
374
|
-
medium_test_data_structure.to_yaml
|
|
375
|
-
end
|
|
376
|
-
|
|
377
|
-
def generate_large_yaml
|
|
378
|
-
data = large_test_data_structure
|
|
379
|
-
data[:dataset][:header][:format] = 'yaml'
|
|
380
|
-
data.to_yaml
|
|
381
|
-
end
|
|
382
|
-
|
|
383
|
-
# TOML test data generators
|
|
384
|
-
def generate_small_toml
|
|
385
|
-
data = small_test_data_structure
|
|
386
|
-
<<~TOML
|
|
387
|
-
[config]
|
|
388
|
-
|
|
389
|
-
[config.database]
|
|
390
|
-
host = "#{data[:config][:database][:host]}"
|
|
391
|
-
port = #{data[:config][:database][:port]}
|
|
392
|
-
name = "#{data[:config][:database][:name]}"
|
|
393
|
-
user = "#{data[:config][:database][:user]}"
|
|
394
|
-
password = "#{data[:config][:database][:password]}"
|
|
395
|
-
|
|
396
|
-
[config.cache]
|
|
397
|
-
enabled = #{data[:config][:cache][:enabled]}
|
|
398
|
-
ttl = #{data[:config][:cache][:ttl]}
|
|
399
|
-
TOML
|
|
400
|
-
end
|
|
401
|
-
|
|
402
|
-
def generate_medium_toml
|
|
403
|
-
data = medium_test_data_structure
|
|
404
|
-
# Use smaller dataset for TOML due to verbosity
|
|
405
|
-
users = data[:users].first(100)
|
|
406
|
-
users.map do |user|
|
|
407
|
-
<<~USER
|
|
408
|
-
[[users]]
|
|
409
|
-
id = #{user[:id]}
|
|
410
|
-
name = "#{user[:name]}"
|
|
411
|
-
email = "#{user[:email]}"
|
|
412
|
-
created_at = "#{user[:created_at]}"
|
|
413
|
-
|
|
414
|
-
[users.profile]
|
|
415
|
-
age = #{user[:profile][:age]}
|
|
416
|
-
city = "#{user[:profile][:city]}"
|
|
417
|
-
|
|
418
|
-
[users.profile.preferences]
|
|
419
|
-
theme = "#{user[:profile][:preferences][:theme]}"
|
|
420
|
-
notifications = #{user[:profile][:preferences][:notifications]}
|
|
421
|
-
USER
|
|
422
|
-
end.join("\n")
|
|
423
|
-
end
|
|
424
|
-
|
|
425
|
-
def generate_large_toml
|
|
426
|
-
data = large_test_data_structure
|
|
427
|
-
# Use smaller dataset for TOML due to verbosity
|
|
428
|
-
records = data[:dataset][:records].first(1000)
|
|
429
|
-
records_toml = records.map do |record|
|
|
430
|
-
<<~RECORD
|
|
431
|
-
[[dataset.records]]
|
|
432
|
-
id = #{record[:id]}
|
|
433
|
-
timestamp = "#{record[:timestamp]}"
|
|
434
|
-
|
|
435
|
-
[dataset.records.data]
|
|
436
|
-
field1 = "#{record[:data][:field1]}"
|
|
437
|
-
field2 = #{record[:data][:field2]}
|
|
438
|
-
field3 = "#{record[:data][:field3]}"
|
|
439
|
-
nested = #{record[:data][:nested].inspect}
|
|
440
|
-
|
|
441
|
-
[dataset.records.metadata]
|
|
442
|
-
source = "#{record[:metadata][:source]}"
|
|
443
|
-
version = "#{record[:metadata][:version]}"
|
|
444
|
-
checksum = "#{record[:metadata][:checksum]}"
|
|
445
|
-
RECORD
|
|
446
|
-
end.join("\n")
|
|
447
|
-
|
|
448
|
-
<<~TOML
|
|
449
|
-
[dataset]
|
|
450
|
-
|
|
451
|
-
[dataset.header]
|
|
452
|
-
created = "#{data[:dataset][:header][:created]}"
|
|
453
|
-
count = #{records.length}
|
|
454
|
-
format = "toml"
|
|
455
|
-
|
|
456
|
-
#{records_toml}
|
|
457
|
-
TOML
|
|
168
|
+
def load_test_data
|
|
169
|
+
@test_data = TestData.load(@benchmark_config)
|
|
458
170
|
end
|
|
459
171
|
end
|
|
460
172
|
end
|
|
@@ -190,7 +190,7 @@ module Serialbench
|
|
|
190
190
|
say "🏗️ Generating HTML site for result: #{result_path}", :green
|
|
191
191
|
|
|
192
192
|
# Use the unified site generator for results
|
|
193
|
-
|
|
193
|
+
puts 'Site generation moved to serialbench.github.io'
|
|
194
194
|
|
|
195
195
|
say '✅ HTML site generated successfully!', :green
|
|
196
196
|
say "Site location: #{options[:output_dir]}", :cyan
|
data/lib/serialbench/cli.rb
CHANGED
|
@@ -4,7 +4,6 @@ require 'thor'
|
|
|
4
4
|
require_relative 'cli/base_cli'
|
|
5
5
|
require_relative 'cli/environment_cli'
|
|
6
6
|
require_relative 'cli/benchmark_cli'
|
|
7
|
-
require_relative 'cli/resultset_cli'
|
|
8
7
|
require_relative 'cli/ruby_build_cli'
|
|
9
8
|
require_relative 'cli/validate_cli'
|
|
10
9
|
|
|
@@ -17,9 +16,6 @@ module Serialbench
|
|
|
17
16
|
desc 'benchmark SUBCOMMAND', 'Manage individual benchmark runs'
|
|
18
17
|
subcommand 'benchmark', Serialbench::Cli::BenchmarkCli
|
|
19
18
|
|
|
20
|
-
desc 'resultset SUBCOMMAND', 'Manage benchmark resultsets (collections of runs)'
|
|
21
|
-
subcommand 'resultset', Serialbench::Cli::ResultsetCli
|
|
22
|
-
|
|
23
19
|
desc 'ruby_build SUBCOMMAND', 'Manage Ruby-Build definitions for validation'
|
|
24
20
|
subcommand 'ruby_build', Serialbench::Cli::RubyBuildCli
|
|
25
21
|
|
|
@@ -45,7 +41,6 @@ module Serialbench
|
|
|
45
41
|
COMMANDS:
|
|
46
42
|
environment Manage benchmark environments (Docker, ASDF, Local)
|
|
47
43
|
benchmark Manage individual benchmark runs
|
|
48
|
-
resultset Manage benchmark resultsets (collections of runs)
|
|
49
44
|
ruby-build Manage Ruby-Build definitions for validation
|
|
50
45
|
version Show version information
|
|
51
46
|
help Show this help message
|
|
@@ -62,13 +57,6 @@ module Serialbench
|
|
|
62
57
|
serialbench benchmark create my-benchmark
|
|
63
58
|
serialbench benchmark execute my-benchmark.yml
|
|
64
59
|
|
|
65
|
-
# Create a result set for comparison
|
|
66
|
-
serialbench resultset create comparison-set
|
|
67
|
-
serialbench resultset add-result comparison-set results/my-benchmark
|
|
68
|
-
|
|
69
|
-
# Generate static sites
|
|
70
|
-
serialbench benchmark build-site results/my-benchmark
|
|
71
|
-
serialbench resultset build-site resultsets/comparison-set
|
|
72
60
|
|
|
73
61
|
For detailed help on any command, use:
|
|
74
62
|
serialbench COMMAND help
|
|
@@ -68,6 +68,7 @@ module Serialbench
|
|
|
68
68
|
attribute :generation, IterationPerformance, collection: true
|
|
69
69
|
attribute :memory, MemoryPerformance, collection: true
|
|
70
70
|
attribute :streaming, IterationPerformance, collection: true
|
|
71
|
+
attribute :xpath, IterationPerformance, collection: true
|
|
71
72
|
|
|
72
73
|
key_value do
|
|
73
74
|
map 'serializers', to: :serializers
|
|
@@ -75,6 +76,7 @@ module Serialbench
|
|
|
75
76
|
map 'generation', to: :generation
|
|
76
77
|
map 'memory', to: :memory
|
|
77
78
|
map 'streaming', to: :streaming
|
|
79
|
+
map 'xpath', to: :xpath
|
|
78
80
|
end
|
|
79
81
|
end
|
|
80
82
|
end
|
|
@@ -8,7 +8,6 @@ module Serialbench
|
|
|
8
8
|
class ResultStore
|
|
9
9
|
DEFAULT_BASE_PATH = 'results'
|
|
10
10
|
RUNS_PATH = 'runs'
|
|
11
|
-
SETS_PATH = 'sets'
|
|
12
11
|
|
|
13
12
|
attr_reader :base_path
|
|
14
13
|
|
|
@@ -34,27 +33,6 @@ module Serialbench
|
|
|
34
33
|
limit ? runs.first(limit) : runs
|
|
35
34
|
end
|
|
36
35
|
|
|
37
|
-
# Run set management
|
|
38
|
-
def sets_path
|
|
39
|
-
File.join(@base_path, SETS_PATH)
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
def find_resultsets(tags: nil, limit: nil)
|
|
43
|
-
resultsets = ResultSet.find_all(sets_path)
|
|
44
|
-
|
|
45
|
-
resultsets = resultsets.select { |resultset| (Array(tags) - resultset.tags).empty? } if tags
|
|
46
|
-
|
|
47
|
-
limit ? resultsets.first(limit) : resultsets
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
# Convenience methods
|
|
51
|
-
def create_resultset(name, run_platform_strings, metadata: {})
|
|
52
|
-
run_paths = run_platform_strings.map { |ps| File.join(runs_path, ps) }
|
|
53
|
-
resultset = ResultSet.create(name, run_paths, metadata: metadata)
|
|
54
|
-
save_resultset(resultset)
|
|
55
|
-
resultset
|
|
56
|
-
end
|
|
57
|
-
|
|
58
36
|
# Validation
|
|
59
37
|
def validate_structure
|
|
60
38
|
errors = []
|
|
@@ -62,7 +40,6 @@ module Serialbench
|
|
|
62
40
|
# Check base structure
|
|
63
41
|
errors << "Base path does not exist: #{@base_path}" unless Dir.exist?(@base_path)
|
|
64
42
|
errors << "Runs directory does not exist: #{runs_path}" unless Dir.exist?(runs_path)
|
|
65
|
-
errors << "Sets directory does not exist: #{sets_path}" unless Dir.exist?(sets_path)
|
|
66
43
|
|
|
67
44
|
# Validate individual runs
|
|
68
45
|
if Dir.exist?(runs_path)
|
|
@@ -78,20 +55,6 @@ module Serialbench
|
|
|
78
55
|
end
|
|
79
56
|
end
|
|
80
57
|
|
|
81
|
-
# Validate result sets
|
|
82
|
-
if Dir.exist?(sets_path)
|
|
83
|
-
Dir.glob(File.join(sets_path, '*')).each do |set_path|
|
|
84
|
-
next unless Dir.exist?(set_path)
|
|
85
|
-
|
|
86
|
-
begin
|
|
87
|
-
resultset = ResultSet.load(set_path)
|
|
88
|
-
resultset.validate!
|
|
89
|
-
rescue StandardError => e
|
|
90
|
-
errors << "Invalid result set at #{set_path}: #{e.message}"
|
|
91
|
-
end
|
|
92
|
-
end
|
|
93
|
-
end
|
|
94
|
-
|
|
95
58
|
errors
|
|
96
59
|
end
|
|
97
60
|
|
|
@@ -101,7 +64,6 @@ module Serialbench
|
|
|
101
64
|
|
|
102
65
|
def ensure_results_directory
|
|
103
66
|
FileUtils.mkdir_p(runs_path) unless Dir.exist?(runs_path)
|
|
104
|
-
FileUtils.mkdir_p(sets_path) unless Dir.exist?(sets_path)
|
|
105
67
|
end
|
|
106
68
|
end
|
|
107
69
|
end
|
data/lib/serialbench/models.rb
CHANGED
|
@@ -6,7 +6,6 @@ require_relative 'models/result'
|
|
|
6
6
|
require_relative 'models/result_store'
|
|
7
7
|
require_relative 'models/benchmark_config'
|
|
8
8
|
require_relative 'models/environment_config'
|
|
9
|
-
require_relative 'models/result_set'
|
|
10
9
|
|
|
11
10
|
module Serialbench
|
|
12
11
|
module Models
|
|
@@ -46,9 +45,5 @@ module Serialbench
|
|
|
46
45
|
def self.create_run(platform_string, benchmark_data, metadata: {})
|
|
47
46
|
Result.create(platform_string, benchmark_data, metadata: metadata)
|
|
48
47
|
end
|
|
49
|
-
|
|
50
|
-
def self.create_resultset(name, run_paths_or_objects, metadata: {})
|
|
51
|
-
ResultSet.create(name, run_paths_or_objects, metadata: metadata)
|
|
52
|
-
end
|
|
53
48
|
end
|
|
54
49
|
end
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
require 'set'
|
|
2
|
+
|
|
1
3
|
# frozen_string_literal: true
|
|
2
4
|
|
|
3
5
|
require 'singleton'
|
|
@@ -33,9 +35,15 @@ module Serialbench
|
|
|
33
35
|
result
|
|
34
36
|
end
|
|
35
37
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
38
|
+
# Capabilities: a Set of symbols declaring what this adapter can do.
|
|
39
|
+
# Subclasses override to add symbols; the base handles every query.
|
|
40
|
+
# This replaces the 12+ individual supports_X? predicates.
|
|
41
|
+
def capabilities
|
|
42
|
+
Set.new(%i[dom parse generate])
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def supports?(capability)
|
|
46
|
+
capabilities.include?(capability)
|
|
39
47
|
end
|
|
40
48
|
|
|
41
49
|
def require_library(library_name)
|
|
@@ -19,32 +19,20 @@ module Serialbench
|
|
|
19
19
|
generate(object, options)
|
|
20
20
|
end
|
|
21
21
|
|
|
22
|
+
def capabilities
|
|
23
|
+
super | Set.new(%i[pretty_print])
|
|
24
|
+
end
|
|
25
|
+
|
|
22
26
|
# JSON-specific features
|
|
23
27
|
def features
|
|
24
28
|
{
|
|
25
|
-
pretty_print:
|
|
26
|
-
streaming:
|
|
27
|
-
symbol_keys:
|
|
28
|
-
custom_types:
|
|
29
|
+
pretty_print: supports?(:pretty_print),
|
|
30
|
+
streaming: supports?(:sax) || supports?(:streaming),
|
|
31
|
+
symbol_keys: supports?(:symbol_keys),
|
|
32
|
+
custom_types: supports?(:custom_types)
|
|
29
33
|
}
|
|
30
34
|
end
|
|
31
35
|
|
|
32
|
-
def supports_generation?
|
|
33
|
-
true
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
def supports_pretty_print?
|
|
37
|
-
true
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
def supports_symbol_keys?
|
|
41
|
-
false
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
def supports_custom_types?
|
|
45
|
-
false
|
|
46
|
-
end
|
|
47
|
-
|
|
48
36
|
# Subclasses should override this to specify their library name
|
|
49
37
|
def library_require_name
|
|
50
38
|
raise NotImplementedError, 'Subclasses must implement #library_require_name'
|
|
@@ -34,22 +34,6 @@ module Serialbench
|
|
|
34
34
|
JSON.generate(object)
|
|
35
35
|
end
|
|
36
36
|
end
|
|
37
|
-
|
|
38
|
-
def supports_streaming?
|
|
39
|
-
false
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
def supports_pretty_print?
|
|
43
|
-
true
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
def supports_symbol_keys?
|
|
47
|
-
false
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
def supports_custom_types?
|
|
51
|
-
false
|
|
52
|
-
end
|
|
53
37
|
end
|
|
54
38
|
end
|
|
55
39
|
end
|
|
@@ -46,23 +46,8 @@ module Serialbench
|
|
|
46
46
|
super
|
|
47
47
|
end
|
|
48
48
|
|
|
49
|
-
def
|
|
50
|
-
|
|
51
|
-
Oj.respond_to?(:saj_parse)
|
|
52
|
-
rescue LoadError
|
|
53
|
-
false
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
def supports_pretty_print?
|
|
57
|
-
true
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
def supports_symbol_keys?
|
|
61
|
-
true
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
def supports_custom_types?
|
|
65
|
-
true
|
|
49
|
+
def capabilities
|
|
50
|
+
super | Set.new(%i[symbol_keys custom_types sax])
|
|
66
51
|
end
|
|
67
52
|
end
|
|
68
53
|
|