serialbench 0.2.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/.github/scripts/push-to-data.sh +65 -0
  3. data/.github/workflows/benchmark.yml +106 -160
  4. data/.github/workflows/release.yml +68 -13
  5. data/Gemfile +1 -1
  6. data/config/benchmarks/full-json.yml +25 -0
  7. data/config/benchmarks/full-toml.yml +25 -0
  8. data/config/benchmarks/full-xml.yml +26 -0
  9. data/config/benchmarks/full-yaml.yml +25 -0
  10. data/config/benchmarks/full.yml +9 -4
  11. data/lib/serialbench/benchmark_runner.rb +20 -23
  12. data/lib/serialbench/cli/benchmark_cli.rb +1 -1
  13. data/lib/serialbench/cli.rb +1 -8
  14. data/lib/serialbench/models/benchmark_result.rb +2 -0
  15. data/lib/serialbench/serializers/base_serializer.rb +14 -2
  16. data/lib/serialbench/serializers/xml/base_xml_serializer.rb +6 -10
  17. data/lib/serialbench/serializers/xml/leptris_serializer.rb +15 -8
  18. data/lib/serialbench/serializers/xml/nokogiri_serializer.rb +2 -7
  19. data/lib/serialbench/serializers/xml/oga_serializer.rb +4 -0
  20. data/lib/serialbench/version.rb +1 -1
  21. data/lib/serialbench.rb +1 -1
  22. metadata +8 -38
  23. data/lib/serialbench/templates/assets/css/benchmark_report.css +0 -535
  24. data/lib/serialbench/templates/assets/css/format_based.css +0 -474
  25. data/lib/serialbench/templates/assets/css/themes.css +0 -589
  26. data/lib/serialbench/templates/assets/js/chart_helpers.js +0 -411
  27. data/lib/serialbench/templates/assets/js/dashboard.js +0 -795
  28. data/lib/serialbench/templates/assets/js/navigation.js +0 -142
  29. data/lib/serialbench/templates/base.liquid +0 -49
  30. data/lib/serialbench/templates/format_based.liquid +0 -507
  31. data/lib/serialbench/templates/partials/chart_section.liquid +0 -4
  32. data/site/.gitignore +0 -5
  33. data/site/README.md +0 -32
  34. data/site/astro.config.mjs +0 -12
  35. data/site/package-lock.json +0 -6160
  36. data/site/package.json +0 -22
  37. data/site/public/favicon.svg +0 -4
  38. data/site/scripts/gen-sample-data.mjs +0 -154
  39. data/site/src/components/AvailabilityMatrix.astro +0 -61
  40. data/site/src/components/CalibratedLeaderboard.vue +0 -134
  41. data/site/src/components/CitationBox.vue +0 -50
  42. data/site/src/components/DashboardConsole.vue +0 -193
  43. data/site/src/components/FeaturesTable.astro +0 -97
  44. data/site/src/components/MemoryPanel.vue +0 -44
  45. data/site/src/components/OpsChart.vue +0 -135
  46. data/site/src/config.ts +0 -18
  47. data/site/src/layouts/Layout.astro +0 -85
  48. data/site/src/lib/channels.ts +0 -24
  49. data/site/src/lib/dashboard.ts +0 -269
  50. data/site/src/pages/index.astro +0 -44
  51. data/site/src/pages/library/[slug].astro +0 -128
  52. data/site/src/pages/methodology.astro +0 -55
  53. data/site/src/styles/global.css +0 -103
  54. data/site/tsconfig.json +0 -5
@@ -1,44 +0,0 @@
1
- ---
2
- import Layout from '../layouts/Layout.astro';
3
- import DashboardConsole from '../components/DashboardConsole.vue';
4
- import AvailabilityMatrix from '../components/AvailabilityMatrix.astro';
5
- import FeaturesTable from '../components/FeaturesTable.astro';
6
- import payloadData from '../data/sample.json';
7
- import type { Payload } from '../lib/dashboard';
8
-
9
- const payload = payloadData as unknown as Payload;
10
- const envCount = Object.keys(payload.environments).length;
11
- const runDate = payload.metadata.generated_at.slice(0, 10);
12
- ---
13
-
14
- <Layout title="Serialbench — Ruby serialization benchmarks">
15
- <section class="mb-10">
16
- <p class="font-mono text-[10px] uppercase tracking-[0.14em] text-phosphor mb-3">Measurement console · run {runDate}</p>
17
- <h1 class="text-3xl sm:text-4xl font-semibold tracking-tight max-w-3xl">
18
- How fast can Ruby read and write your data?
19
- </h1>
20
- <p class="mt-3 text-inkdim max-w-2xl leading-relaxed">
21
- Every XML, JSON, YAML and TOML serializer in the ecosystem, measured on the same workloads across
22
- {envCount}
23
- platform and Ruby combinations. Pick a format, pin a reference, and read the field.
24
- </p>
25
- </section>
26
-
27
- <DashboardConsole client:load payload={payload} />
28
-
29
- <AvailabilityMatrix payload={payload} />
30
-
31
- <FeaturesTable payload={payload} />
32
-
33
- <section class="mt-12 border border-line bg-panel px-5 py-5">
34
- <h2 class="font-mono text-[10px] uppercase tracking-[0.14em] text-inkmute mb-2">Raw data</h2>
35
- <p class="text-sm text-inkdim max-w-2xl leading-relaxed">
36
- Full results per environment and the complete resultset are exported as YAML alongside this site —
37
- <span class="font-mono text-[13px] break-all">data/&lt;environment&gt;.yaml</span>
38
- and
39
- <span class="font-mono text-[13px] break-all">data/resultset.yaml</span>. Re-run any leg with
40
- <span class="font-mono text-[13px] break-all">serialbench environment execute</span>
41
- using the configs in the repository.
42
- </p>
43
- </section>
44
- </Layout>
@@ -1,128 +0,0 @@
1
- ---
2
- import Layout from '../../layouts/Layout.astro';
3
- import payloadData from '../../data/sample.json';
4
- import type { Payload } from '../../lib/dashboard';
5
- import {
6
- availability,
7
- environmentLabel,
8
- environmentList,
9
- formatIps,
10
- formatsForSerializer,
11
- libraryStats,
12
- OPERATION_LABELS,
13
- primaryEnvKey,
14
- runnerShortLabel,
15
- } from '../../lib/dashboard';
16
- import { channelColor } from '../../lib/channels';
17
- import { BASE } from '../../config';
18
-
19
- export function getStaticPaths() {
20
- const payload = payloadData as unknown as Payload;
21
- return availability(payload).serializers.map((serializer) => ({
22
- params: { slug: serializer },
23
- }));
24
- }
25
-
26
- const { slug } = Astro.params;
27
- const payload = payloadData as unknown as Payload;
28
- const serializer = slug as string;
29
-
30
- const { envKeys, cells } = availability(payload);
31
- const envs = environmentList(payload);
32
- const envKey = primaryEnvKey(payload);
33
- const stats = libraryStats(payload, serializer, envKey);
34
- const formats = formatsForSerializer(payload, serializer);
35
- const covered = cells.get(serializer) ?? new Set<string>();
36
- const rankOne = stats.filter((s) => s.rank === 1).length;
37
- const color = channelColor(serializer);
38
- const runDate = payload.metadata.generated_at.slice(0, 10);
39
- ---
40
-
41
- <Layout title={`${serializer} — Serialbench`}>
42
- <section class="mb-8">
43
- <p class="font-mono text-[10px] uppercase tracking-[0.14em] text-inkmute mb-3">
44
- <a href={BASE} class="hover:text-phosphor transition-colors">← Field overview</a>
45
- </p>
46
- <div class="flex items-center gap-3">
47
- <span class="h-3 w-3 rounded-full" style={`background: ${color}`}></span>
48
- <h1 class="text-3xl font-semibold tracking-tight font-mono">{serializer}</h1>
49
- </div>
50
- <p class="mt-2 text-inkdim">
51
- {formats.map((f) => f.toUpperCase()).join(' / ')} serializer ·
52
- measured on {covered.size} of {envKeys.length} environments ·
53
- <span class="text-phosphor">#1 in {rankOne} of {stats.length} measurements</span>
54
- <span class="text-inkmute"> · primary environment: {environmentLabel(envKey, payload.environments[envKey])}</span>
55
- </p>
56
- </section>
57
-
58
- <section aria-label="Availability" class="mb-10 border border-line bg-panel px-4 py-4">
59
- <p class="font-mono text-[10px] uppercase tracking-[0.14em] text-inkmute mb-3">Measured environments</p>
60
- <div class="flex flex-wrap gap-x-4 gap-y-2">
61
- {
62
- envs.map(({ key, env }) => (
63
- <span class="font-mono text-[11px] flex items-center gap-1.5" title={environmentLabel(key, env)}>
64
- {covered.has(key) ? (
65
- <span class="h-2 w-2 rounded-full" style={`background: ${color}`} />
66
- ) : (
67
- <span class="h-2 w-2 rounded-full border border-line" />
68
- )}
69
- <span class={covered.has(key) ? 'text-inkdim' : 'text-inkmute line-through'}>{runnerShortLabel(key, env)}</span>
70
- </span>
71
- ))
72
- }
73
- </div>
74
- </section>
75
-
76
- <section aria-label="Measurements">
77
- <p class="font-mono text-[10px] uppercase tracking-[0.14em] text-inkmute mb-3">
78
- Iterations per second on the primary environment · rank vs the field
79
- </p>
80
- <div class="overflow-x-auto border border-line bg-panel">
81
- <table class="w-full border-collapse">
82
- <thead>
83
- <tr class="font-mono text-[10px] uppercase tracking-[0.14em] text-inkmute">
84
- <th class="text-left font-normal px-3 py-2 border-b border-line">Operation</th>
85
- <th class="text-left font-normal px-3 py-2 border-b border-line">Input</th>
86
- <th class="text-right font-normal px-3 py-2 border-b border-line">ips</th>
87
- <th class="text-right font-normal px-3 py-2 border-b border-line">ms/op</th>
88
- <th class="text-right font-normal px-3 py-2 border-b border-line">Rank</th>
89
- <th class="text-left font-normal px-3 py-2 border-b border-line">vs best</th>
90
- </tr>
91
- </thead>
92
- <tbody>
93
- {
94
- stats.map((s) => (
95
- <tr>
96
- <td class="px-3 py-1.5 border-b border-line/50 font-mono text-[12px]">
97
- {OPERATION_LABELS[s.op as keyof typeof OPERATION_LABELS] ?? s.op}
98
- </td>
99
- <td class="px-3 py-1.5 border-b border-line/50 font-mono text-[12px] text-inkmute">{s.size}</td>
100
- <td class="px-3 py-1.5 border-b border-line/50 font-mono text-[12px] text-right tabular">{formatIps(s.ips)}</td>
101
- <td class="px-3 py-1.5 border-b border-line/50 font-mono text-[12px] text-right tabular text-inkdim">
102
- {(1000 / s.ips).toFixed(s.ips < 10 ? 2 : 1)}
103
- </td>
104
- <td class="px-3 py-1.5 border-b border-line/50 font-mono text-[12px] text-right tabular">
105
- <span class={s.rank === 1 ? 'text-phosphor' : 'text-inkdim'}>{s.rank}/{s.fieldSize}</span>
106
- </td>
107
- <td class="px-3 py-1.5 border-b border-line/50 font-mono text-[11px] text-inkmute whitespace-nowrap">
108
- {s.ratioToBest == null ? '—' : s.rank === 1 ? 'best in field' : `${s.ratioToBest.toFixed(1)}× slower than ${s.bestSerializer}`}
109
- </td>
110
- </tr>
111
- ))
112
- }
113
- </tbody>
114
- </table>
115
- </div>
116
- <p class="font-mono text-[11px] text-inkmute mt-2">
117
- Compare every library on every environment on the <a href={BASE} class="text-phosphor hover:underline">dashboard</a>.
118
- </p>
119
- </section>
120
-
121
- <section class="mt-10 border border-line bg-panel px-4 py-3 flex flex-wrap items-center gap-4">
122
- <span class="font-mono text-[10px] uppercase tracking-[0.14em] text-inkmute shrink-0">Cite</span>
123
- <p class="flex-1 min-w-64 font-mono text-[12px] leading-relaxed text-inkdim">
124
- {serializer} measured on {environmentLabel(envKey, payload.environments[envKey])}, {formats.map((f) => f.toUpperCase()).join('/')}
125
- workloads — serialbench, {runDate}. Full data: data/{envKey}.yaml.
126
- </p>
127
- </section>
128
- </Layout>
@@ -1,55 +0,0 @@
1
- ---
2
- import Layout from '../layouts/Layout.astro';
3
- import { SITE } from '../config';
4
- ---
5
-
6
- <Layout title="Methodology — Serialbench">
7
- <section class="mb-10">
8
- <p class="font-mono text-[10px] uppercase tracking-[0.14em] text-phosphor mb-3">Calibration</p>
9
- <h1 class="text-3xl sm:text-4xl font-semibold tracking-tight">Methodology</h1>
10
- <p class="mt-3 text-inkdim max-w-2xl leading-relaxed">
11
- A benchmark you can't reproduce is marketing. This page documents exactly how serialbench
12
- produces every number on this site.
13
- </p>
14
- </section>
15
-
16
- <div class="grid gap-6 md:grid-cols-2 max-w-4xl">
17
- <section class="border border-line bg-panel p-5">
18
- <h2 class="font-mono text-[11px] uppercase tracking-[0.14em] text-phosphor mb-3">Workloads</h2>
19
- <p class="text-sm text-inkdim leading-relaxed">
20
- Each serializer parses, generates, and (where supported) streams the same documents at each
21
- configured size. Operations run after warmup iterations; throughput is reported as
22
- iterations per second, memory as allocated and retained bytes measured around the operation.
23
- </p>
24
- </section>
25
-
26
- <section class="border border-line bg-panel p-5">
27
- <h2 class="font-mono text-[11px] uppercase tracking-[0.14em] text-phosphor mb-3">Environments</h2>
28
- <p class="text-sm text-inkdim leading-relaxed">
29
- Results are collected per GitHub-hosted runner platform and Ruby version, then aggregated into
30
- one resultset. Runners differ in CPU and memory — compare within an environment, not across
31
- environments, unless you are comparing environments.
32
- </p>
33
- </section>
34
-
35
- <section class="border border-line bg-panel p-5">
36
- <h2 class="font-mono text-[11px] uppercase tracking-[0.14em] text-phosphor mb-3">Fairness limits</h2>
37
- <p class="text-sm text-inkdim leading-relaxed">
38
- Libraries that fail to load or lack an operation are skipped for that leg rather than counted
39
- as zero. A dot missing from the availability matrix means no measurement exists — never assume
40
- a score. Microbenchmarks measure these workloads, not yours.
41
- </p>
42
- </section>
43
-
44
- <section class="border border-line bg-panel p-5">
45
- <h2 class="font-mono text-[11px] uppercase tracking-[0.14em] text-phosphor mb-3">Reproduce</h2>
46
- <p class="text-sm text-inkdim leading-relaxed">
47
- Clone
48
- <a class="text-phosphor hover:underline" href={SITE.repoUrl}>{SITE.repoUrl.replace("https://github.com/", "")}</a>,
49
- then run
50
- <span class="font-mono text-[13px]">bundle exec serialbench environment execute &lt;env&gt; &lt;benchmark&gt; &lt;out&gt;</span>.
51
- The raw YAML next to every chart is the exact output of that command.
52
- </p>
53
- </section>
54
- </div>
55
- </Layout>
@@ -1,103 +0,0 @@
1
- @import 'tailwindcss';
2
-
3
- /*
4
- * serialbench design tokens — instrument console.
5
- * Semantic colors are runtime CSS vars so [data-theme] can retarget them;
6
- * @theme inline exposes them to Tailwind utilities (bg-panel, text-inkdim…).
7
- */
8
- :root {
9
- --surface: #0b101b;
10
- --panel: #111a2b;
11
- --panel-2: #16223a;
12
- --line: #22314e;
13
- --ink: #e9eef6;
14
- --ink-dim: #a9b6c9;
15
- --ink-mute: #6f7f99;
16
- --phosphor: #5fd4e8;
17
- --phosphor-ink: #071018;
18
- --grid: rgba(95, 212, 232, 0.07);
19
- --grid-strong: rgba(95, 212, 232, 0.14);
20
- }
21
-
22
- [data-theme='light'] {
23
- --surface: #f3f5f9;
24
- --panel: #ffffff;
25
- --panel-2: #eaeef4;
26
- --line: #d5dce7;
27
- --ink: #16202e;
28
- --ink-dim: #46566b;
29
- --ink-mute: #75849d;
30
- --phosphor: #0b7d96;
31
- --phosphor-ink: #ffffff;
32
- --grid: rgba(11, 125, 150, 0.08);
33
- --grid-strong: rgba(11, 125, 150, 0.16);
34
- }
35
-
36
- @theme inline {
37
- --color-surface: var(--surface);
38
- --color-panel: var(--panel);
39
- --color-panel2: var(--panel-2);
40
- --color-line: var(--line);
41
- --color-ink: var(--ink);
42
- --color-inkdim: var(--ink-dim);
43
- --color-inkmute: var(--ink-mute);
44
- --color-phosphor: var(--phosphor);
45
- --color-phosphorink: var(--phosphor-ink);
46
- --font-sans: 'IBM Plex Sans', ui-sans-serif, system-ui, sans-serif;
47
- --font-mono: 'IBM Plex Mono', ui-monospace, 'SF Mono', monospace;
48
- }
49
-
50
- html {
51
- color-scheme: dark;
52
- }
53
- [data-theme='light'] {
54
- color-scheme: light;
55
- }
56
-
57
- body {
58
- background-color: var(--surface);
59
- color: var(--ink);
60
- font-family: var(--font-sans);
61
- -webkit-font-smoothing: antialiased;
62
- }
63
-
64
- /* scope graticule: faint calibration grid used behind bars and panels */
65
- .graticule {
66
- background-image:
67
- linear-gradient(to right, var(--grid) 1px, transparent 1px),
68
- linear-gradient(to bottom, var(--grid) 1px, transparent 1px);
69
- background-size: 3rem 3rem;
70
- }
71
-
72
- .tabular {
73
- font-variant-numeric: tabular-nums;
74
- }
75
-
76
- /* trace sweep-in for the leaderboard; disabled for reduced motion */
77
- @keyframes trace-in {
78
- from {
79
- transform: scaleX(0);
80
- }
81
- to {
82
- transform: scaleX(1);
83
- }
84
- }
85
- .trace-in {
86
- transform-origin: left center;
87
- animation: trace-in 0.7s cubic-bezier(0.22, 1, 0.36, 1) both;
88
- }
89
- @media (prefers-reduced-motion: reduce) {
90
- .trace-in {
91
- animation: none;
92
- }
93
- }
94
-
95
- ::selection {
96
- background: var(--phosphor);
97
- color: var(--phosphor-ink);
98
- }
99
-
100
- :focus-visible {
101
- outline: 2px solid var(--phosphor);
102
- outline-offset: 2px;
103
- }
data/site/tsconfig.json DELETED
@@ -1,5 +0,0 @@
1
- {
2
- "extends": "astro/tsconfigs/strict",
3
- "include": [".astro/types.d.ts", "**/*"],
4
- "exclude": ["dist"]
5
- }