Skip to content

Commit ff9b14b

Browse files
feat(bench): simplify suites and enrich comparison metadata
- introduce shared task runner (`bench/lib/task-runner.js`) and suite hub (`bench/suites.js`), then refactor `run-full suite`, `run-quick-suite`, and the CLI to use the unified pipeline instead of bespoke scripts - extract fixture metadata (title, domain, link/script/table counts, word count) for every benchmarked file and persist it in `bench/.results/comparison-latest.json`; console summaries now show domain/context and absolute deltas alongside ratios - treat benchmark outputs/fixtures as first-class tracked artifacts by adding `.results` and root `fixtures` to Git LFS; update `.gitignore` so those directories remain visible while developers can opt out via LFS - update README and benchmark artifacts to reflect the new metadata ordering and fresh results
1 parent 9a25c34 commit ff9b14b

133 files changed

Lines changed: 182156 additions & 3470 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitattributes

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
tests/fixtures/** filter=lfs diff=lfs merge=lfs -text
2+
bench/datasets/** filter=lfs diff=lfs merge=lfs -text
3+
bench/.results/** filter=lfs diff=lfs merge=lfs -text
4+
fixtures/** filter=lfs diff=lfs merge=lfs -text

.gitignore

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,13 @@ node_modules
22
.DS_Store
33
dist
44
coverage
5-
.bench
65
bench/output
7-
fixtures/
6+
bench/artifacts/
7+
bench/datasets/
8+
# Local benchmark scratch space
9+
.bench
10+
# Allow tracked benchmark results and fixtures (managed via Git LFS)
11+
# (keep optional local fixture packs elsewhere)
812
.vscode
913
.idea
1014
*.log

README.md

Lines changed: 117 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -101,40 +101,49 @@ const options: Options = {
101101
## Benchmarks
102102

103103
<!-- BENCHMARK:START -->
104-
<!-- Last updated: 2025-09-27T19:41:03.406Z -->
104+
<!-- Last updated: 2025-09-28T15:53:58.306Z -->
105105

106106
## Performance
107107

108-
🏆 **h2m-parser is the FASTEST converter!**
108+
**Runtime ranking (lower is better):**
109+
1. h2m-parser — 1.488ms
110+
2. mdream — 1.519ms
111+
3. Turndown — 7.527ms
112+
4. node-html-markdown — 154.666ms
109113

110114
<details>
111115
<summary>📊 Benchmark Results (click to expand)</summary>
112116

113117
### Benchmark Methodology
114118

115-
- **Dataset:** 91 files (4 synthetic + 87 real HTML documents)
116-
- **File sizes:** 21KB to 380KB (mean: ~100KB)
117-
- **Iterations:** 30 per file for statistical significance
118-
- **Total runtime:** 70.7 seconds
119+
- **Dataset:** 93 files (4 synthetic + 89 real HTML documents)
120+
- **Dataset path:** tests/fixtures
121+
- **File sizes:** 21KB to 1771KB (mean: ~119KB)
122+
- **Iterations:** 100 per file for statistical significance
123+
- **Total runtime:** 737.2 seconds
119124
- **Environment:** Node.js with standard V8 optimizations
120125

121126
### Average Processing Time
122127

123-
Tested across 91 files in tests/fixtures (up to 380KB):
128+
Tested across 93 files in tests/fixtures (up to 1771KB):
124129

125-
| Library | Without Readability | With Readability | Performance |
126-
|---------|---------------------|------------------|-------------|
127-
| **h2m-parser** | **1.464ms**| 7.094ms | **Fastest** |
128-
| Turndown | 5.430ms | ❌ Not supported | 3.71x slower |
129-
| node-html-markdown | 3.547ms | ❌ Not supported | 2.42x slower |
130+
| Library | Without Readability | With Readability | Relative |
131+
|---------|---------------------|------------------|----------|
132+
| **h2m-parser**| **1.488ms** | 14.199ms | **Fastest** |
133+
| mdream | 1.519ms | ❌ Not supported | 1.02x slower |
134+
| Turndown | 7.527ms | ❌ Not supported | 5.06x slower |
135+
| node-html-markdown | 154.666ms | ❌ Not supported | 103.94x slower |
130136

131-
**Readability overhead:** +5.630ms (enables article extraction + content cleaning)
137+
**Readability overhead (h2m-parser):** +12.711ms (enables article extraction + content cleaning)
132138

133139
### Performance Analysis
134140

135-
- **h2m-parser vs Turndown:** 3.71x faster (5.430ms → 1.464ms)
136-
- **h2m-parser vs node-html-markdown:** 2.42x faster (3.547ms → 1.464ms)
137-
- **Readability impact:** 4.8x slower when enabled (1.464ms → 7.094ms)
141+
- **Fastest baseline:** h2m-parser averages 1.488ms per document without Readability.
142+
- **h2m-parser vs Turndown:** 5.06x faster (7.527ms → 1.488ms)
143+
- **h2m-parser vs node-html-markdown:** 103.94x faster (154.666ms → 1.488ms)
144+
- **h2m-parser vs mdream:** 1.02x faster (1.519ms → 1.488ms)
145+
- **Readability impact:** 9.5x slower when enabled (1.488ms → 14.199ms)
146+
- **Token savings vs raw HTML:** 24051 tokens saved (95.63%) on tests/fixtures/039c4b966d1f2a0c589ac0aad211fe65500ad1cb58c7f45b34251db7056803ec.html.
138147
- **Algorithmic complexity:** O(n) linear scaling confirmed across file sizes
139148

140149
### Performance Projections
@@ -143,90 +152,138 @@ Estimated processing times for different file sizes (without Readability):
143152

144153
```
145154
100KB 1ms
146-
1MB 15ms
147-
10MB 150ms
148-
100MB 1.5s
155+
1MB 13ms
156+
10MB 128ms
157+
100MB 1.3s
149158
```
150159

151-
*Based on linear scaling from 100KB average file size at 1.464ms*
160+
*Based on linear scaling from 119KB average file size at 1.488ms*
152161

153162
### Detailed Results by File Size
154163

155-
Sample results showing performance across different file types and sizes:
156-
157164
#### tiny (18 bytes)
158165

159166
| Library | Mean (ms) | P95 (ms) | P99 (ms) |
160167
|---------|-----------|----------|----------|
161-
| h2m-parser (no Readability) | 0.017 | 0.022 | 0.025 |
162-
| h2m-parser (with Readability) | 0.221 | 0.254 | 0.263 |
163-
| Turndown | 0.024 | 0.029 | 0.030 |
164-
| node-html-markdown | 0.013 | 0.019 | 0.020 |
168+
| h2m-parser (no Readability) | 0.021 | 0.032 | 0.034 |
169+
| h2m-parser (with Readability) | 0.264 | 0.416 | 0.456 |
170+
| Turndown | 0.023 | 0.043 | 0.049 |
171+
| node-html-markdown | 0.011 | 0.017 | 0.020 |
172+
| Mdream | 0.005 | 0.010 | 0.014 |
165173

166174
#### small (84 bytes)
167175

168176
| Library | Mean (ms) | P95 (ms) | P99 (ms) |
169177
|---------|-----------|----------|----------|
170-
| h2m-parser (no Readability) | 0.028 | 0.037 | 0.046 |
171-
| h2m-parser (with Readability) | 0.213 | 0.262 | 0.271 |
172-
| Turndown | 0.049 | 0.065 | 0.071 |
173-
| node-html-markdown | 0.029 | 0.035 | 0.036 |
178+
| h2m-parser (no Readability) | 0.016 | 0.025 | 0.027 |
179+
| h2m-parser (with Readability) | 0.195 | 0.279 | 0.295 |
180+
| Turndown | 0.039 | 0.051 | 0.057 |
181+
| node-html-markdown | 0.021 | 0.028 | 0.030 |
182+
| Mdream | 0.013 | 0.016 | 0.017 |
174183

175184
#### medium (369 bytes)
176185

177186
| Library | Mean (ms) | P95 (ms) | P99 (ms) |
178187
|---------|-----------|----------|----------|
179-
| h2m-parser (no Readability) | 0.017 | 0.021 | 0.022 |
180-
| h2m-parser (with Readability) | 0.238 | 0.285 | 0.291 |
181-
| Turndown | 0.050 | 0.062 | 0.063 |
182-
| node-html-markdown | 0.028 | 0.033 | 0.035 |
188+
| h2m-parser (no Readability) | 0.013 | 0.016 | 0.017 |
189+
| h2m-parser (with Readability) | 0.233 | 0.266 | 0.274 |
190+
| Turndown | 0.048 | 0.055 | 0.055 |
191+
| node-html-markdown | 0.022 | 0.026 | 0.026 |
192+
| Mdream | 0.022 | 0.041 | 0.041 |
183193

184-
#### file_1 (88KB)
194+
#### file_42 (21KB)
185195

186196
| Library | Mean (ms) | P95 (ms) | P99 (ms) |
187197
|---------|-----------|----------|----------|
188-
| h2m-parser (no Readability) | 1.544 | 1.785 | 1.808 |
189-
| h2m-parser (with Readability) | 7.220 | 10.383 | 11.360 |
190-
| Turndown | 6.053 | 7.549 | 7.699 |
191-
| node-html-markdown | 3.387 | 3.854 | 3.880 |
198+
| h2m-parser (no Readability) | 0.271 | 0.298 | 0.313 |
199+
| h2m-parser (with Readability) | 1.802 | 1.928 | 1.972 |
200+
| Turndown | 1.391 | 1.494 | 1.545 |
201+
| node-html-markdown | 0.405 | 0.431 | 0.444 |
202+
| Mdream | 0.350 | 0.411 | 0.413 |
192203

193-
#### file_2 (69KB)
204+
#### file_33 (88KB)
194205

195206
| Library | Mean (ms) | P95 (ms) | P99 (ms) |
196207
|---------|-----------|----------|----------|
197-
| h2m-parser (no Readability) | 1.228 | 1.354 | 1.361 |
198-
| h2m-parser (with Readability) | 5.250 | 5.967 | 7.073 |
199-
| Turndown | 4.365 | 4.597 | 5.881 |
200-
| node-html-markdown | 2.017 | 2.233 | 2.236 |
208+
| h2m-parser (no Readability) | 1.042 | 1.155 | 1.168 |
209+
| h2m-parser (with Readability) | 5.941 | 6.381 | 8.880 |
210+
| Turndown | 5.847 | 7.082 | 7.125 |
211+
| node-html-markdown | 3.068 | 3.367 | 3.436 |
212+
| Mdream | 1.904 | 1.973 | 2.011 |
201213

202-
#### file_3 (157KB)
214+
#### file_89 (1771KB)
203215

204216
| Library | Mean (ms) | P95 (ms) | P99 (ms) |
205217
|---------|-----------|----------|----------|
206-
| h2m-parser (no Readability) | 3.117 | 3.348 | 3.412 |
207-
| h2m-parser (with Readability) | 12.775 | 17.030 | 17.198 |
208-
| Turndown | 9.051 | 11.514 | 11.553 |
209-
| node-html-markdown | 3.901 | 4.036 | 4.079 |
218+
| h2m-parser (no Readability) | 33.908 | 37.195 | 38.000 |
219+
| h2m-parser (with Readability) | 645.631 | 916.483 | 938.031 |
220+
| Turndown | 192.002 | 198.932 | 199.174 |
221+
| node-html-markdown | 14049.153 | 14410.806 | 14431.159 |
222+
| Mdream | 49.764 | 50.351 | 50.357 |
223+
224+
*See [`bench/comparison-results.md`](bench/comparison-results.md) for complete results across all 93 files*
225+
226+
### Workflow Comparison (Await vs Stream)
227+
228+
| Mode | Iterations | Mean (ms) | p95 (ms) | Min (ms) | Max (ms) |
229+
|------|------------|-----------|----------|----------|----------|
230+
| h2m-parser (await) | 10 | 14.74 | 66.10 | 7.11 | 66.10 |
231+
| mdream (await) | 10 | 4.20 | 13.95 | 1.87 | 13.95 |
232+
| mdream (stream) | 10 | 14.89 | 118.69 | 2.15 | 118.69 |
233+
234+
### Token Savings
235+
236+
- Model: gpt-4o-mini
237+
- HTML tokens: 25151
238+
- Markdown tokens: 1100
239+
- Savings: 24051 tokens (95.63%)
240+
- Estimated cost delta per document: $0.003608
241+
- Markdown length: 4869 characters
242+
243+
### Memory Snapshot
244+
245+
- Mode: h2m-reuse
246+
- Iterations: 10
247+
- RSS change: 47.08 MB
248+
249+
### Bundle Size Snapshot
250+
251+
Generated: 2025-09-28T15:53:59.056Z
252+
253+
| File | Size | Gzipped | Δ Size | Δ Gzipped |
254+
|------|------|---------|--------|-----------|
255+
| cli.cjs | 22KB | 8KB | +0 B (+0.00%) | +0 B (+0.00%) |
256+
| cli.mjs | 22KB | 8KB | +0 B (+0.00%) | +0 B (+0.00%) |
257+
| index.cjs | 19KB | 7KB | +0 B (+0.00%) | +0 B (+0.00%) |
258+
| index.mjs | 19KB | 7KB | +0 B (+0.00%) | +0 B (+0.00%) |
259+
260+
### Live Fetch Results
261+
262+
Fetched: https://en.wikipedia.org/wiki/Markdown
210263

211-
*See [`bench/comparison-results.md`](bench/comparison-results.md) for complete results across all 91 files*
264+
| Tool | Mean | Min | Max |
265+
|------|------|-----|-----|
266+
| h2m-parser | 51.74ms | 44.38ms | 65.76ms |
267+
| mdream (await) | 6.48ms | 3.99ms | 10.85ms |
268+
| mdream (stream) | 13.04ms | 11.51ms | 15.98ms |
212269

213270
### Feature Comparison
214271

215-
| Feature | h2m-parser | Turndown | node-html-markdown |
216-
|---------|------|----------|--------------------|
217-
| **Performance** | ✅ Fastest || ⚠️ |
218-
| **Readability** ||||
219-
| **Link cleanup** ||||
220-
| **Front matter** ||||
221-
| **Chunking** ||||
222-
| **TypeScript** ||||
223-
| **Streaming** ||||
272+
| Feature | h2m-parser | Turndown | node-html-markdown | mdream |
273+
|---------|------------|----------|--------------------|--------|
274+
| **Performance** | ✅ Fastest |+406% slower | ❌ +10294% slower | ⚠️ +2% slower |
275+
| **Readability** |||| ⚠️ |
276+
| **Link cleanup** |||| ⚠️ |
277+
| **Front matter** |||||
278+
| **Chunking** |||| ⚠️ |
279+
| **TypeScript** |||||
280+
| **Streaming** |||||
224281

225282
### Benchmark Transparency
226283

227284
- **Raw results:** [`bench/.results/comparison-latest.json`](bench/.results/comparison-latest.json)
228285
- **Benchmark runner:** [`bench/compare.js`](bench/compare.js)
229-
- **Test dataset:** [`tests/fixtures/`](tests/fixtures/) (87 real HTML files)
286+
- **Test dataset:** [`tests/fixtures/`](tests/fixtures/) (89 real HTML files)
230287
- **Statistical data:** Includes mean, median, P95, P99, min/max for each test
231288
- **Reproducible:** Run `pnpm bench:compare:full` to verify results
232289

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
{
2+
"generatedAt": "2025-09-28T15:53:59.056Z",
3+
"files": [
4+
{
5+
"name": "cli.cjs",
6+
"size": 22660,
7+
"gzipSize": 8168,
8+
"sizeDiff": 0,
9+
"gzipDiff": 0,
10+
"sizeDiffPct": 0,
11+
"gzipDiffPct": 0
12+
},
13+
{
14+
"name": "cli.mjs",
15+
"size": 22210,
16+
"gzipSize": 8071,
17+
"sizeDiff": 0,
18+
"gzipDiff": 0,
19+
"sizeDiffPct": 0,
20+
"gzipDiffPct": 0
21+
},
22+
{
23+
"name": "index.cjs",
24+
"size": 19540,
25+
"gzipSize": 6969,
26+
"sizeDiff": 0,
27+
"gzipDiff": 0,
28+
"sizeDiffPct": 0,
29+
"gzipDiffPct": 0
30+
},
31+
{
32+
"name": "index.mjs",
33+
"size": 19314,
34+
"gzipSize": 6942,
35+
"sizeDiff": 0,
36+
"gzipDiff": 0,
37+
"sizeDiffPct": 0,
38+
"gzipDiffPct": 0
39+
}
40+
]
41+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
{
2+
"generatedAt": "2025-09-28T15:53:59.056Z",
3+
"files": [
4+
{
5+
"name": "cli.cjs",
6+
"size": 22660,
7+
"gzipSize": 8168,
8+
"sizeDiff": 0,
9+
"gzipDiff": 0,
10+
"sizeDiffPct": 0,
11+
"gzipDiffPct": 0
12+
},
13+
{
14+
"name": "cli.mjs",
15+
"size": 22210,
16+
"gzipSize": 8071,
17+
"sizeDiff": 0,
18+
"gzipDiff": 0,
19+
"sizeDiffPct": 0,
20+
"gzipDiffPct": 0
21+
},
22+
{
23+
"name": "index.cjs",
24+
"size": 19540,
25+
"gzipSize": 6969,
26+
"sizeDiff": 0,
27+
"gzipDiff": 0,
28+
"sizeDiffPct": 0,
29+
"gzipDiffPct": 0
30+
},
31+
{
32+
"name": "index.mjs",
33+
"size": 19314,
34+
"gzipSize": 6942,
35+
"sizeDiff": 0,
36+
"gzipDiff": 0,
37+
"sizeDiffPct": 0,
38+
"gzipDiffPct": 0
39+
}
40+
]
41+
}

0 commit comments

Comments
 (0)