serialbench 0.12.0 → 0.12.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fb1d328cda41b77014e07bcca03086cf860a81a9f02c8863918e37d41ab1e201
4
- data.tar.gz: 889ba7216bdc343cf5886f2fdaf6257452633161cd291c66e54823caa4cfbb7d
3
+ metadata.gz: '06086099f5bb25ceb1e784b26ef5a2e66bc0529b4d96beae9a19fa33c8039ef4'
4
+ data.tar.gz: 0f3ab5f9d4fddac28db8f239ba0e2aec955133a91251906177aac8139291d248
5
5
  SHA512:
6
- metadata.gz: 64d0c977c7efffaa614ac03425eae1cf5c3e83d44f06bc2ba98da8b03671644887ec8813d8c506987e6d0f0ce74424af5bf74bf025ad9a09303f7d57a0c3ffa4
7
- data.tar.gz: 4db8775576b87ff4eb268f063e323527f5f639d3c65c55f12f02078d7772ff552dc67578f048179a7c17d0a33a08058527959f96f2b0403595ce349c4bb9c79c
6
+ metadata.gz: 8eb041f1fce782a7a1ba78ac8bb73ecce811174da90b5aadcebf763669013de8925b8970cc76e75d9338ef1ea9f304db53f1b9d4a660712b6fc57883b57370b8
7
+ data.tar.gz: 47257d11443f97212634770660f8a3e2f80e268c48712d3d26d22470d9530f3fe7ab99924a17a1922306d5e8649f0d65aaf60b31d01aa09be969300aa1fb355d
@@ -31,28 +31,49 @@ echo "Pushing ${TARGET_PATH} to serialbench/data..."
31
31
 
32
32
  # Try create; 409 (branch race) and 422 (exists) both retry with fresh sha
33
33
  put_file() {
34
- local sha_arg=()
35
- [ -n "${1:-}" ] && sha_arg=(-d "{\"message\": \"${PLATFORM} ruby-${RUBY_VERSION} ${FMT} (update)\", \"content\": \"${CONTENT}\", \"sha\": \"$1\", \"branch\": \"main\"}")
36
- [ -n "${1:-}" ] || sha_arg=(-d "{\"message\": \"${PLATFORM} ruby-${RUBY_VERSION} ${FMT}\", \"content\": \"${CONTENT}\", \"branch\": \"main\"}")
34
+ # The body goes through a file: passing it as a curl argument hits the
35
+ # ~32KB exec limit on windows/msys once a results.yaml grows ("Argument
36
+ # list too long" -> curl never runs -> the format silently drops).
37
+ local body
38
+ body=$(mktemp /tmp/push-body.XXXXXX)
39
+ if [ -n "${1:-}" ]; then
40
+ printf '{"message": "%s (update)", "content": "%s", "sha": "%s", "branch": "main"}' \
41
+ "${PLATFORM} ruby-${RUBY_VERSION} ${FMT}" "$CONTENT" "$1" > "$body"
42
+ else
43
+ printf '{"message": "%s", "content": "%s", "branch": "main"}' \
44
+ "${PLATFORM} ruby-${RUBY_VERSION} ${FMT}" "$CONTENT" > "$body"
45
+ fi
37
46
  curl -s -o /tmp/data-push-resp.json -w "%{http_code}" -X PUT \
38
47
  -H "Authorization: Bearer ${DATA_TOKEN}" \
39
48
  -H "Accept: application/vnd.github+json" \
40
49
  "https://api.github.com/repos/serialbench/data/contents/${TARGET_PATH}" \
41
- "${sha_arg[@]}"
50
+ --data-binary @"$body" || echo 000
51
+ rm -f "$body"
52
+ }
53
+
54
+ # Sha of the existing data file, empty on any failure — guarded so a dead
55
+ # pipe (curl -f, grep miss, SIGPIPE under pipefail) cannot kill the leg.
56
+ fetch_sha() {
57
+ curl -s -H "Authorization: Bearer ${DATA_TOKEN}" \
58
+ "https://api.github.com/repos/serialbench/data/contents/${TARGET_PATH}" \
59
+ | grep -m1 -oE '"sha": ?"[a-f0-9]{40}"' | grep -oE '[a-f0-9]{40}' || true
42
60
  }
43
61
 
44
62
  HTTP_CODE=$(put_file "")
45
63
  for TRY in 1 2 3 4 5; do
46
64
  if [ "$HTTP_CODE" = "200" ] || [ "$HTTP_CODE" = "201" ]; then break; fi
47
65
  if [ "$HTTP_CODE" = "409" ] || [ "$HTTP_CODE" = "422" ]; then
48
- SHA=$(curl -sf \
49
- -H "Authorization: Bearer ${DATA_TOKEN}" \
50
- "https://api.github.com/repos/serialbench/data/contents/${TARGET_PATH}" \
51
- | grep -oE '"sha":\s*"[a-f0-9]+"' | head -1 | grep -oE '[a-f0-9]{40}')
66
+ SHA=$(fetch_sha)
52
67
  [ -z "$SHA" ] && break
53
68
  echo "attempt $TRY got $HTTP_CODE — refetching sha and retrying"
54
69
  sleep $((TRY * 3))
55
70
  HTTP_CODE=$(put_file "$SHA")
71
+ elif [ "$HTTP_CODE" = "000" ]; then
72
+ # 000 = no HTTP response at all (connection-level blip). Retrying is
73
+ # the only recovery — breaking here silently drops the format.
74
+ echo "attempt $TRY got 000 (no response) — retrying"
75
+ sleep $((TRY * 5))
76
+ HTTP_CODE=$(put_file "")
56
77
  else
57
78
  break
58
79
  fi
@@ -65,19 +86,18 @@ elif [ "$HTTP_CODE" = "422" ]; then
65
86
  # moves between fetch and put) is retried with a fresh sha.
66
87
  UPDATE_CODE=409
67
88
  for TRY in 1 2 3; do
68
- SHA=$(curl -sf \
69
- -H "Authorization: Bearer ${DATA_TOKEN}" \
70
- "https://api.github.com/repos/serialbench/data/contents/${TARGET_PATH}" \
71
- | grep -oE '"sha":\s*"[a-f0-9]+"' | head -1 | grep -oE '[a-f0-9]{40}')
89
+ SHA=$(fetch_sha)
72
90
  if [ -z "$SHA" ]; then
73
91
  echo "::warning::422 but couldn't get sha for ${TARGET_PATH}"
74
92
  break
75
93
  fi
94
+ printf '{"message": "%s (update)", "content": "%s", "sha": "%s", "branch": "main"}' \
95
+ "${PLATFORM} ruby-${RUBY_VERSION} ${FMT}" "$CONTENT" "${SHA}" > /tmp/push-body.json
76
96
  UPDATE_CODE=$(curl -s -o /dev/null -w "%{http_code}" -X PUT \
77
97
  -H "Authorization: Bearer ${DATA_TOKEN}" \
78
98
  -H "Accept: application/vnd.github+json" \
79
99
  "https://api.github.com/repos/serialbench/data/contents/${TARGET_PATH}" \
80
- -d "{\"message\": \"${PLATFORM} ruby-${RUBY_VERSION} ${FMT} (update)\", \"content\": \"${CONTENT}\", \"sha\": \"${SHA}\", \"branch\": \"main\"}")
100
+ --data-binary @/tmp/push-body.json)
81
101
  [ "$UPDATE_CODE" = "200" ] && break
82
102
  echo "update attempt $TRY got $UPDATE_CODE — refetching sha"
83
103
  sleep 2
@@ -179,6 +179,41 @@ jobs:
179
179
  run: .github/scripts/push-to-data.sh json ${{ matrix.ruby-version }} ${{ matrix.platform }}
180
180
  shell: bash
181
181
 
182
+ - name: Benchmark cbor
183
+ continue-on-error: true
184
+ shell: bash
185
+ env:
186
+ GITHUB_RUNNER_PLATFORM: ${{ matrix.platform }}
187
+ DATA_REPO_TOKEN: ${{ secrets.DATA_REPO_TOKEN }}
188
+ DATA_RUN_DATE: ${{ needs.setup.outputs.run-date }}
189
+ run: |
190
+ if [ "${{ github.run_attempt }}" -gt 1 ] && .github/scripts/format-status.sh cbor ${{ matrix.ruby-version }} ${{ matrix.platform }}; then
191
+ echo "cbor already in the data repo — skipping (gap-filling rerun)"
192
+ exit 0
193
+ fi
194
+ bundle exec serialbench environment execute \
195
+ config/environments/ci-ruby-${{ matrix.ruby-version }}.yml \
196
+ config/benchmarks/full-cbor.yml \
197
+ "results/runs/ci-ruby-${{ matrix.ruby-version }}-${{ matrix.platform }}/cbor"
198
+
199
+ - name: Upload cbor
200
+ if: ${{ !cancelled() }}
201
+ uses: actions/upload-artifact@v4
202
+ with:
203
+ name: benchmark-results-${{ matrix.platform }}-ruby-${{ matrix.ruby-version }}-cbor
204
+ path: results/runs/ci-ruby-${{ matrix.ruby-version }}-${{ matrix.platform }}/cbor
205
+ retention-days: 30
206
+ if-no-files-found: warn
207
+
208
+ - name: Push cbor to data repo
209
+ if: ${{ !cancelled() }}
210
+ continue-on-error: true
211
+ env:
212
+ DATA_REPO_TOKEN: ${{ secrets.DATA_REPO_TOKEN }}
213
+ DATA_RUN_DATE: ${{ needs.setup.outputs.run-date }}
214
+ run: .github/scripts/push-to-data.sh cbor ${{ matrix.ruby-version }} ${{ matrix.platform }}
215
+ shell: bash
216
+
182
217
  - name: Benchmark yaml
183
218
  continue-on-error: true
184
219
  shell: bash
@@ -331,7 +366,7 @@ jobs:
331
366
  DATA_RUN_DATE: ${{ needs.setup.outputs.run-date }}
332
367
  run: |
333
368
  MISSING=""
334
- for FMT in json yaml toml html xml; do
369
+ for FMT in json yaml toml html xml cbor; do
335
370
  if .github/scripts/format-status.sh "$FMT" "${{ matrix.ruby-version }}" "${{ matrix.platform }}"; then
336
371
  echo "$FMT: in data repo"
337
372
  else
data/Gemfile CHANGED
@@ -12,12 +12,13 @@ unless Gem.win_platform? && RUBY_PLATFORM.include?('aarch64')
12
12
  gem 'libxml-ruby'
13
13
  end
14
14
 
15
- gem 'leptris', '1.9.193.4' # html5 conformance campaign + memchr tokenizer perf
16
- gem 'yeptris', '0.6.5.4' # compiled plan walk; dump round-trip fixed (plain keys)
17
- gem 'teptris', '0.2.36' # full platform matrix incl. mingw - windows coverage unblocked
15
+ gem 'leptris', '1.9.380.1' # parse_html defaults :html4 (nokogiri-parity); :whatwg priced separately
16
+ gem 'yeptris', '0.6.36.0' # JSON + CBOR load/dump; faces load standalone (#300, fixed 0.6.35.2)
17
+ gem 'teptris', '0.3.6.3' # 0.3.x line (native-doc API); Teptris::TOML.load/dump unchanged
18
+ gem 'cbor' # RFC 7049 CBOR codec
18
19
  gem 'benchmark' # Removed from stdlib in Ruby 4.0
19
20
  gem 'base64' # Required for Ruby 3.4+
20
- gem 'lutaml-model', '~> 0.7'
21
+ gem 'lutaml-model', '~> 0.8'
21
22
  gem 'octokit'
22
23
  gem 'rake'
23
24
  gem 'rspec'
data/README.adoc CHANGED
@@ -9,7 +9,7 @@ image:https://img.shields.io/github/issues-pr-raw/serialbench/serialbench.svg["P
9
9
 
10
10
  Serialbench is a comprehensive benchmarking suite that evaluates the performance of popular Ruby serialization libraries across multiple formats. It provides detailed performance comparisons and analysis to help developers make informed decisions when choosing serialization libraries for their Ruby applications.
11
11
 
12
- **Supported Formats**: XML, JSON, YAML, TOML, and more
12
+ **Supported Formats**: XML, HTML, JSON, YAML, TOML, and CBOR
13
13
 
14
14
  **Key Metrics**: Parsing speed, generation speed, memory usage, streaming capabilities, and feature completeness
15
15
 
@@ -23,74 +23,129 @@ Serialbench is a comprehensive benchmarking suite that evaluates the performance
23
23
 
24
24
  | XML
25
25
  | https://github.com/ohler55/ox[Ox]
26
- | v2.14.23
26
+ | 2.14.29
27
27
  | C extension XML parser
28
28
 
29
29
  | XML
30
30
  | https://github.com/xml4r/libxml-ruby[LibXML]
31
- | v4.1.2
31
+ | 6.0.0
32
32
  | Ruby bindings for libxml2
33
33
 
34
34
  | XML
35
35
  | https://github.com/sparklemotion/nokogiri[Nokogiri]
36
- | v1.18.8
36
+ | 1.19.4
37
37
  | XML/HTML parser with XPath and CSS selectors
38
38
 
39
39
  | XML
40
40
  | https://github.com/YorickPeterse/oga[Oga]
41
- | v3.4
41
+ | 3.5
42
42
  | Pure Ruby XML parser with XPath support
43
43
 
44
44
  | XML
45
45
  | https://github.com/leptris/leptris-ruby[Leptris]
46
- | v1.1.0
47
- | FFI binding to libleptris (pure-C XML 1.0, XPath 1.0, SAX)
46
+ | 1.9.379.0
47
+ | FFI binding to libleptris (pure-C XML 1.0, XPath/XQuery/XSLT, SAX)
48
48
 
49
49
  | XML
50
50
  | https://github.com/ruby/rexml[REXML]
51
- | v3.4.1
51
+ | 3.4.4
52
52
  | Ruby's standard library XML parser
53
53
 
54
+ | XML
55
+ | https://github.com/Saxonica/Saxon-HE[Saxon-HE]
56
+ | 12.10
57
+ | Reference XSLT 2.0/3.0 engine, invoked through its Java CLI (`SAXON_JAR`)
58
+
59
+ | HTML
60
+ | Nokogiri
61
+ | 1.19.4
62
+ | HTML parsing through the same gem as the XML adapter
63
+
64
+ | HTML
65
+ | Oga
66
+ | 3.5
67
+ | Pure Ruby HTML parsing
68
+
69
+ | HTML
70
+ | https://github.com/leptris/leptris-ruby[Leptris]
71
+ | 1.9.379.0
72
+ | HTML 4 engine (libxml2/Nokogiri-compatible shape)
73
+
74
+ | HTML
75
+ | https://github.com/leptris/leptris-ruby[Leptris-whatwg]
76
+ | 1.9.379.0
77
+ | WHATWG-conformant HTML engine, priced separately
78
+
54
79
  | JSON
55
80
  | https://github.com/ohler55/oj[Oj]
56
- | v3.16.11
81
+ | 3.17.6
57
82
  | JSON parser with multiple parsing modes
58
83
 
59
84
  | JSON
60
85
  | https://github.com/brianmario/yajl-ruby[YAJL]
61
- | v1.4.3
86
+ | 1.4.3
62
87
  | JSON library with streaming capabilities
63
88
 
64
89
  | JSON
65
90
  | https://github.com/flori/json[JSON]
66
- | v2.12.2
91
+ | 3.1.0
67
92
  | Ruby's standard library JSON parser
68
93
 
94
+ | JSON
95
+ | https://github.com/glycerine/rapidjson[rapidjson]
96
+ | 0.4.0
97
+ | Ruby bindings for RapidJSON
98
+
99
+ | JSON
100
+ | https://github.com/leptris/yeptris-ruby[yeptris-json]
101
+ | 0.6.34.3
102
+ | FFI binding to libyeptris' native JSON materializer
103
+
69
104
  | YAML
70
105
  | https://github.com/ruby/psych[Psych]
71
- | v5.1.2
106
+ | bundled
72
107
  | Ruby's standard library YAML parser
73
108
 
74
109
  | YAML
75
110
  | https://github.com/ruby/syck[Syck]
76
- | v1.5.1.1
111
+ | 1.6.2
77
112
  | Legacy YAML parser
78
113
 
114
+ | YAML
115
+ | https://github.com/leptris/yeptris-ruby[yeptris-yaml]
116
+ | 0.6.34.3
117
+ | FFI binding to libyeptris' YAML face
118
+
119
+ | CBOR
120
+ | https://github.com/cbor-ruby/cbor[cbor]
121
+ | 0.5.10.3
122
+ | RFC 7049 CBOR codec
123
+
124
+ | CBOR
125
+ | https://github.com/leptris/yeptris-ruby[yeptris-cbor]
126
+ | 0.6.34.3
127
+ | FFI binding to libyeptris' CBOR face (parse-only)
128
+
79
129
  | TOML
80
130
  | https://github.com/fbernier/tomlib[Tomlib]
81
- | v0.7.3
131
+ | 0.7.3
82
132
  | TOML parser implemented in C
83
133
 
84
134
  | TOML
85
135
  | https://github.com/emancu/toml-rb[TOML-RB]
86
- | v2.2.0
136
+ | 4.2.1
87
137
  | Pure Ruby TOML parser
88
138
 
89
139
  | TOML
90
140
  | https://github.com/fbernier/tomlrb[tomlrb]
91
- | v2.0.3
141
+ | 2.0.4
92
142
  | A Racc based TOML Ruby parser (Only supports parsing, no support for dumping/writing.)
93
143
 
144
+ | TOML
145
+ | https://github.com/leptris/teptris-ruby[Teptris]
146
+ | 0.3.6.3
147
+ | Native C extension over libteptris (C99 TOML parser/emitter)
148
+
94
149
  |===
95
150
 
96
151
 
@@ -101,9 +156,8 @@ different formats for single-environment and multi-environment runs.
101
156
 
102
157
  The data formats include:
103
158
 
104
- * **Single benchmark results**: Individual benchmark run output
105
- * **Result set data structure**: Multi-platform benchmark aggregation
106
- * **JSON schema specification**: Complete schema validation rules
159
+ * **Single benchmark results**: One YAML file per platform × Ruby × format
160
+ * **JSON schema specification**: Schema validation via `serialbench validate`
107
161
  * **Configuration file formats**: Docker and ASDF configuration examples
108
162
 
109
163
  == Prerequisites
@@ -192,8 +246,8 @@ $ gem install serialbench
192
246
 
193
247
  == Command line interface
194
248
 
195
- Serialbench provides a comprehensive Thor-based CLI with four main subcommands
196
- for managing environments, benchmarks, result sets, and Ruby builds.
249
+ Serialbench provides a Thor-based CLI with subcommands for managing
250
+ environments, benchmarks, Ruby builds, and schema validation.
197
251
 
198
252
  === Main Commands Overview
199
253
 
@@ -208,8 +262,8 @@ USAGE:
208
262
  COMMANDS:
209
263
  environment Manage benchmark environments (Docker, ASDF, Local)
210
264
  benchmark Manage individual benchmark runs
211
- resultset Manage benchmark resultsets (collections of runs)
212
- ruby-build Manage Ruby-Build definitions for validation
265
+ ruby_build Manage Ruby-Build definitions for validation
266
+ validate Validate YAML files against schemas
213
267
  version Show version information
214
268
  help Show this help message
215
269
 
@@ -217,21 +271,16 @@ EXAMPLES:
217
271
  # Create a Docker environment
218
272
  serialbench environment new docker-test docker
219
273
 
220
- # Run multi-environment benchmarks
221
- serialbench environment multi-execute asdf --config=serialbench-asdf.yml
222
- serialbench environment multi-execute docker --config=serialbench-docker.yml
274
+ # Run a benchmark in an environment
275
+ serialbench environment execute config/environments/docker-test.yml config/benchmarks/full-xml.yml results/runs/docker-test/xml
223
276
 
224
277
  # Create and execute a benchmark
225
278
  serialbench benchmark create my-benchmark
226
- serialbench benchmark execute my-benchmark.yml
227
-
228
- # Create a result set for comparison
229
- serialbench resultset create comparison-set
230
- serialbench resultset add-result comparison-set results/my-benchmark
279
+ serialbench benchmark execute config/environments/local-dev.yml config/runs/my-benchmark.yml
231
280
 
232
- # Generate static sites
233
- serialbench benchmark build-site results/my-benchmark
234
- serialbench resultset build-site resultsets/comparison-set
281
+ # Validate YAML files against the bundled schemas
282
+ serialbench validate result results/runs/my-run/xml/results.yaml
283
+ serialbench validate config config/benchmarks/full.yml
235
284
  ----
236
285
 
237
286
  === Environment management
@@ -270,23 +319,6 @@ The `_docker_execute` command is a private command used internally by the
270
319
  `execute` command to run benchmarks in Docker environments.
271
320
 
272
321
 
273
- === Result set management
274
-
275
- The `resultset` subcommand manages collections of benchmark runs for comparison analysis.
276
-
277
- [source]
278
- ----
279
- $ serialbench resultset help
280
- Commands:
281
- serialbench resultset add-result RESULT_PATH RESULTSET_PATH # Add a run to a resultset
282
- serialbench resultset build-site RESULTSET_PATH [OUTPUT_DIR] # Generate HTML site for a resultset
283
- serialbench resultset create NAME PATH # Create a new resultset
284
- serialbench resultset help [COMMAND] # Describe subcommands or one specific subcommand
285
- serialbench resultset list # List all available resultsets
286
- serialbench resultset remove-result RESULTSET_PATH RESULT_PATH # Remove a run from a resultset
287
- ----
288
-
289
-
290
322
  === ruby-build management
291
323
 
292
324
  The `ruby-build` subcommand manages Ruby build definitions and version information.
@@ -322,20 +354,11 @@ $ bundle exec serialbench environment prepare config/environments/docker-ruby-3.
322
354
  # 2. Run benchmark
323
355
  $ bundle exec serialbench environment execute config/environments/docker-ruby-3.1.yml config/benchmarks/short.yml results/runs/docker-ruby-3.1-results
324
356
 
325
- # 3. Create a resultset
326
- $ bundle exec serialbench resultset create docker-comparison results/sets/docker-comparison
327
-
328
- # 3a. (Optional) Build the site from the result if you want to visualize results
329
- $ bundle exec serialbench benchmark build-site results/runs/docker-ruby-3.1-results/ --output_dir=_site_result
357
+ # 3. Inspect the per-format results
358
+ $ ls results/runs/docker-ruby-3.1-results/xml/ # one results.yaml per format
330
359
 
331
- # 4. Add the result to the resultset
332
- $ bundle exec serialbench resultset add-result results/sets/docker-comparison/ results/runs/docker-ruby-3.1-results/
333
-
334
- # 5. Build the site from the resultset
335
- $ bundle exec serialbench resultset build-site results/sets/docker-comparison/
336
-
337
- # 6. Open the generated site
338
- $ open _site/index.html
360
+ # CI pushes each format to the serialbench/data repo as it completes; the
361
+ # dashboard (serialbench/serialbench.github.io) builds from that repo.
339
362
  ----
340
363
 
341
364
  === ASDF-based testing
@@ -456,33 +479,6 @@ warmup: 3
456
479
 
457
480
  == Results structure
458
481
 
459
- === Individual run results
460
-
461
- Results are stored in a structured directory format, with each run containing
462
- raw benchmark data and execution logs.
463
-
464
- The directory is located at `results/runs/{name}/`, where `{name}` is the name
465
- of the environment used for the benchmark.
466
-
467
- [source]
468
- ----
469
- results/runs/docker-ruby-33-results/
470
- ├── results.yaml # Raw benchmark data
471
- └── benchmark.log # Execution log
472
- ----
473
-
474
- === ResultSet structure
475
-
476
- ResultSets aggregate multiple benchmark runs for comparison. They are stored in
477
- a structured directory format at `results/sets/{name}/`, where `{name}` is the
478
- name of the result set.
479
-
480
- [source]
481
- ----
482
- results/sets/ruby-version-comparison/
483
- └── resultset.yml # Result set configuration
484
- ----
485
-
486
482
  == Benchmark categories
487
483
 
488
484
  === Parsing performance
@@ -0,0 +1,23 @@
1
+ # Per-format split of full.yml: each format runs in its own process so
2
+ # memory profiling of large documents cannot compound across formats.
3
+ name: full-cbor
4
+
5
+ data_sizes:
6
+ - small
7
+ - medium
8
+ - large
9
+
10
+ formats:
11
+ - cbor
12
+
13
+ iterations:
14
+ small: 10
15
+ medium: 3
16
+ large: 1
17
+
18
+ operations:
19
+ - parsing
20
+ - generation
21
+ - memory
22
+
23
+ warmup: 2
@@ -0,0 +1,34 @@
1
+ type: object
2
+ title: Serialbench Benchmark Config
3
+ description: Schema for benchmark configuration files (basic structure validation)
4
+ properties:
5
+ name:
6
+ type: string
7
+ description:
8
+ type: string
9
+ data_sizes:
10
+ type: array
11
+ items:
12
+ type: string
13
+ enum: [small, medium, large]
14
+ formats:
15
+ type: array
16
+ items:
17
+ type: string
18
+ enum: [xml, html, json, yaml, toml, cbor]
19
+ iterations:
20
+ type: object
21
+ properties:
22
+ small:
23
+ type: integer
24
+ medium:
25
+ type: integer
26
+ large:
27
+ type: integer
28
+ warmup:
29
+ type: integer
30
+ operations:
31
+ type: array
32
+ items:
33
+ type: string
34
+ enum: [parsing, generation, xpath, xquery, xslt, xslt30, validation, streaming, memory]
@@ -0,0 +1,27 @@
1
+ type: object
2
+ title: Serialbench Environment Config
3
+ description: Schema for benchmark environment configuration files (basic structure validation)
4
+ properties:
5
+ name:
6
+ type: string
7
+ kind:
8
+ type: string
9
+ enum: [local, docker, asdf]
10
+ created_at:
11
+ type: string
12
+ ruby_build_tag:
13
+ type: string
14
+ description:
15
+ type: string
16
+ docker:
17
+ type: object
18
+ properties:
19
+ image:
20
+ type: string
21
+ dockerfile:
22
+ type: string
23
+ asdf:
24
+ type: object
25
+ properties:
26
+ auto_install:
27
+ type: boolean
@@ -0,0 +1,16 @@
1
+ type: object
2
+ title: Serialbench Resultset
3
+ description: Schema for benchmark resultset files (basic structure validation)
4
+ properties:
5
+ name:
6
+ type: string
7
+ description:
8
+ type: string
9
+ created_at:
10
+ type: string
11
+ updated_at:
12
+ type: string
13
+ results:
14
+ type: array
15
+ items:
16
+ type: object
@@ -170,7 +170,7 @@ _DEFAULT_XSLT30_STYLESHEET = <<'XSL30'.freeze
170
170
  </xsl:stylesheet>
171
171
  XSL30
172
172
 
173
- XPATH_QUERIES = ['//book', "//book[@id='101']", '//book[price > 30]/title'].freeze
173
+ XPATH_QUERIES = ['//user | //record', "//user[@id='101']", '//preferences/theme'].freeze
174
174
  XQUERY_EXPRESSIONS = ['count(//user | //record)', "//record[@id='101']/data/field1", '//user[profile/age > 40]/name'].freeze
175
175
  RNG_SCHEMA = File.expand_path('test_data/schema.rng', Dir.pwd).then { |p| File.exist?(p) ? File.read(p) : _DEFAULT_RNG_SCHEMA }
176
176
  XSLT_STYLESHEET = File.expand_path('test_data/transform.xsl', Dir.pwd).then { |p| File.exist?(p) ? File.read(p) : _DEFAULT_XSLT_STYLESHEET }
@@ -48,7 +48,7 @@ module Serialbench
48
48
  attribute :name, :string
49
49
  attribute :description, :string
50
50
  attribute :data_sizes, :string, collection: true, values: %w[small medium large]
51
- attribute :formats, :string, collection: true, values: %w[xml html json yaml toml]
51
+ attribute :formats, :string, collection: true, values: %w[xml html json yaml toml cbor]
52
52
  attribute :iterations, BenchmarkIteration
53
53
  attribute :warmup, :integer, default: -> { 1 }
54
54
  attribute :operations, :string, collection: true, values: %w[parsing generation xpath xquery xslt xslt30 validation streaming memory]
@@ -5,7 +5,7 @@ require 'lutaml/model'
5
5
  module Serialbench
6
6
  module Models
7
7
  class SerializerInformation < Lutaml::Model::Serializable
8
- attribute :format, :string, values: %w[xml html json yaml toml]
8
+ attribute :format, :string, values: %w[xml html json yaml toml cbor]
9
9
  attribute :name, :string
10
10
  attribute :version, :string
11
11
  attribute :features, :hash
@@ -20,7 +20,7 @@ module Serialbench
20
20
 
21
21
  class AdapterPerformance < Lutaml::Model::Serializable
22
22
  attribute :adapter, :string
23
- attribute :format, :string, values: %w[xml html json yaml toml]
23
+ attribute :format, :string, values: %w[xml html json yaml toml cbor]
24
24
  attribute :data_size, :string, values: %w[small medium large]
25
25
 
26
26
  key_value do
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../base_serializer'
4
+
5
+ module Serialbench
6
+ module Serializers
7
+ module Cbor
8
+ class BaseCborSerializer < BaseSerializer
9
+ def self.format
10
+ :cbor
11
+ end
12
+
13
+ def features
14
+ {
15
+ streaming: supports?(:streaming),
16
+ canonical: supports?(:canonical)
17
+ }
18
+ end
19
+
20
+ def library_require_name
21
+ raise NotImplementedError, 'Subclasses must implement #library_require_name'
22
+ end
23
+
24
+ def available?
25
+ return @available if defined?(@available)
26
+
27
+ @available = begin
28
+ require library_require_name
29
+ true
30
+ rescue LoadError
31
+ false
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base_cbor_serializer'
4
+
5
+ module Serialbench
6
+ module Serializers
7
+ module Cbor
8
+ # The `cbor` gem (RFC 7049 codec, to_cbor/CBOR.load)
9
+ class CborSerializer < BaseCborSerializer
10
+ def name
11
+ 'cbor'
12
+ end
13
+
14
+ def parse(cbor_bytes)
15
+ require 'cbor'
16
+ CBOR.load(cbor_bytes)
17
+ end
18
+
19
+ def generate(object, _options = {})
20
+ require 'cbor'
21
+ object.to_cbor
22
+ end
23
+
24
+ def version
25
+ return 'unknown' unless available?
26
+
27
+ require 'cbor'
28
+ Gem.loaded_specs['cbor']&.version.to_s
29
+ end
30
+
31
+ def library_require_name
32
+ 'cbor'
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base_cbor_serializer'
4
+
5
+ module Serialbench
6
+ module Serializers
7
+ module Cbor
8
+ # Yeptris CBOR side: load + dump. dump round-trips the canonical
9
+ # fixtures exactly as of 0.6.35.1 (yeptris-ruby#293 — before that,
10
+ # strings shaped like exponent notation encoded as floats; before
11
+ # 0.6.34, dump segfaulted outright, yeptris-ruby#152).
12
+ class YeptrisSerializer < BaseCborSerializer
13
+ def name
14
+ 'yeptris-cbor'
15
+ end
16
+
17
+ def parse(cbor_bytes)
18
+ require 'yeptris/cbor'
19
+ Yeptris::CBOR.load(cbor_bytes)
20
+ end
21
+
22
+ def generate(object, _options = {})
23
+ require 'yeptris/cbor'
24
+ Yeptris::CBOR.dump(object)
25
+ end
26
+
27
+ def capabilities
28
+ Set.new(%i[dom parse generate])
29
+ end
30
+
31
+ def available?
32
+ return @available if defined?(@available)
33
+
34
+ @available = begin
35
+ # Faces bootstrap themselves since yeptris 0.6.35.2
36
+ # (yeptris-ruby#300) — standalone requires are safe.
37
+ require 'yeptris/cbor'
38
+ raise LoadError, 'libyeptris has no CBOR support' unless Yeptris::CBOR.available?
39
+
40
+ Yeptris::CBOR.load("\xf5")
41
+ true
42
+ rescue StandardError, LoadError => e
43
+ warn "#{name} unavailable: #{e.class}: #{e.message}"
44
+ false
45
+ end
46
+ end
47
+
48
+ def version
49
+ return 'unknown' unless available?
50
+
51
+ require 'yeptris'
52
+ Yeptris::VERSION
53
+ end
54
+
55
+ def library_require_name
56
+ 'yeptris'
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -10,8 +10,11 @@ module Serialbench
10
10
  'leptris'
11
11
  end
12
12
 
13
+ # 1.9.201 defaults parse_html to the :html4 engine — the
14
+ # libxml2/Nokogiri-compatible shape. The conformant engine is
15
+ # benchmarked separately as leptris-whatwg.
13
16
  def capabilities
14
- super | Set.new(%i[xpath html5])
17
+ super | Set.new(%i[xpath])
15
18
  end
16
19
 
17
20
  def parse(html_string)
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'leptris_serializer'
4
+
5
+ module Serialbench
6
+ module Serializers
7
+ module Html
8
+ # The WHATWG-conformant HTML engine (:whatwg mode, 1.9.104+):
9
+ # implied-<head> set and foster parenting — html5lib corpus
10
+ # 294/1555. Priced separately from the :html4 parity engine.
11
+ class LeptrisWhatwgSerializer < LeptrisSerializer
12
+ def name
13
+ 'leptris-whatwg'
14
+ end
15
+
16
+ def capabilities
17
+ super | Set.new(%i[html5])
18
+ end
19
+
20
+ def parse(html_string)
21
+ require 'leptris'
22
+ require 'leptris/xml'
23
+ Leptris::XML.parse_html(html_string, mode: :whatwg)
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -5,7 +5,8 @@ require_relative 'base_json_serializer'
5
5
  module Serialbench
6
6
  module Serializers
7
7
  module Json
8
- # Yeptris JSON side: parsing only — no dump in 0.2.x
8
+ # Yeptris JSON side: 0.2.x was parse-only; 0.6.x ships a native
9
+ # dump that round-trips the canonical fixtures exactly.
9
10
  class YeptrisSerializer < BaseJsonSerializer
10
11
  def name
11
12
  'yeptris-json'
@@ -16,8 +17,13 @@ module Serialbench
16
17
  Yeptris::JSON.load(json_string)
17
18
  end
18
19
 
20
+ def generate(object, _options = {})
21
+ require 'yeptris'
22
+ Yeptris::JSON.dump(object)
23
+ end
24
+
19
25
  def capabilities
20
- Set.new(%i[dom parse])
26
+ Set.new(%i[dom parse generate])
21
27
  end
22
28
 
23
29
  def available?
@@ -18,6 +18,7 @@ require_relative 'serializers/html/base_html_serializer'
18
18
  require_relative 'serializers/html/nokogiri_serializer'
19
19
  require_relative 'serializers/html/oga_serializer'
20
20
  require_relative 'serializers/html/leptris_serializer'
21
+ require_relative 'serializers/html/leptris_whatwg_serializer'
21
22
 
22
23
  # JSON Serializers
23
24
  require_relative 'serializers/json/base_json_serializer'
@@ -33,6 +34,11 @@ require_relative 'serializers/yaml/psych_serializer'
33
34
  require_relative 'serializers/yaml/syck_serializer'
34
35
  require_relative 'serializers/yaml/yeptris_serializer'
35
36
 
37
+ # CBOR Serializers
38
+ require_relative 'serializers/cbor/base_cbor_serializer'
39
+ require_relative 'serializers/cbor/cbor_serializer'
40
+ require_relative 'serializers/cbor/yeptris_serializer'
41
+
36
42
  # TOML Serializers
37
43
  require_relative 'serializers/toml/base_toml_serializer'
38
44
  require_relative 'serializers/toml/toml_rb_serializer'
@@ -63,7 +69,8 @@ module Serialbench
63
69
  html: [
64
70
  Html::NokogiriSerializer,
65
71
  Html::OgaSerializer,
66
- Html::LeptrisSerializer
72
+ Html::LeptrisSerializer,
73
+ Html::LeptrisWhatwgSerializer
67
74
  ],
68
75
  yaml: [
69
76
  Yaml::PsychSerializer,
@@ -75,6 +82,10 @@ module Serialbench
75
82
  Toml::TomlibSerializer,
76
83
  Toml::TomlrbSerializer,
77
84
  Toml::TeptrisSerializer
85
+ ],
86
+ cbor: [
87
+ Cbor::CborSerializer,
88
+ Cbor::YeptrisSerializer
78
89
  ]
79
90
  }.freeze
80
91
 
@@ -9,6 +9,7 @@ module Serialbench
9
9
 
10
10
  def initialize(config)
11
11
  @config = config
12
+ require 'cbor'
12
13
  end
13
14
 
14
15
  def load
@@ -23,7 +24,7 @@ module Serialbench
23
24
 
24
25
  def fixture_file(size, format)
25
26
  path = "test_data/#{size}.#{format}"
26
- File.read(path) if File.exist?(path)
27
+ File.binread(path) if File.exist?(path)
27
28
  end
28
29
 
29
30
  def generate(key)
@@ -43,10 +44,25 @@ module Serialbench
43
44
  when 'medium.html' then generate_medium_html
44
45
  when 'large.toml' then generate_large_toml
45
46
  when 'large.html' then generate_large_html
47
+ when 'small.cbor' then generate_small_cbor
48
+ when 'medium.cbor' then generate_medium_cbor
49
+ when 'large.cbor' then generate_large_cbor
46
50
  else raise ArgumentError, "no test data generator for #{key}"
47
51
  end
48
52
  end
49
53
 
54
+ def generate_small_cbor
55
+ small_test_data_structure.to_cbor
56
+ end
57
+
58
+ def generate_medium_cbor
59
+ medium_test_data_structure.to_cbor
60
+ end
61
+
62
+ def generate_large_cbor
63
+ large_test_data_structure.to_cbor
64
+ end
65
+
50
66
  # Shared data structure generators
51
67
  def small_test_data_structure
52
68
  {
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Serialbench
4
- VERSION = '0.12.0'
4
+ VERSION = '0.12.2'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: serialbench
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.12.0
4
+ version: 0.12.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose
@@ -270,6 +270,7 @@ files:
270
270
  - Gemfile
271
271
  - README.adoc
272
272
  - Rakefile
273
+ - config/benchmarks/full-cbor.yml
273
274
  - config/benchmarks/full-html.yml
274
275
  - config/benchmarks/full-json.yml
275
276
  - config/benchmarks/full-toml.yml
@@ -284,7 +285,10 @@ files:
284
285
  - config/environments/docker-ruby-3.2.yml
285
286
  - config/environments/docker-ruby-3.3.yml
286
287
  - config/environments/docker-ruby-3.4.yml
288
+ - data/schemas/benchmark_config.yml
289
+ - data/schemas/environment_config.yml
287
290
  - data/schemas/result.yml
291
+ - data/schemas/resultset.yml
288
292
  - docker/Dockerfile.alpine
289
293
  - docker/Dockerfile.ubuntu
290
294
  - docker/README.md
@@ -324,8 +328,12 @@ files:
324
328
  - lib/serialbench/runners/local_runner.rb
325
329
  - lib/serialbench/serializers.rb
326
330
  - lib/serialbench/serializers/base_serializer.rb
331
+ - lib/serialbench/serializers/cbor/base_cbor_serializer.rb
332
+ - lib/serialbench/serializers/cbor/cbor_serializer.rb
333
+ - lib/serialbench/serializers/cbor/yeptris_serializer.rb
327
334
  - lib/serialbench/serializers/html/base_html_serializer.rb
328
335
  - lib/serialbench/serializers/html/leptris_serializer.rb
336
+ - lib/serialbench/serializers/html/leptris_whatwg_serializer.rb
329
337
  - lib/serialbench/serializers/html/nokogiri_serializer.rb
330
338
  - lib/serialbench/serializers/html/oga_serializer.rb
331
339
  - lib/serialbench/serializers/json/base_json_serializer.rb