1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261
|
# RQRCode Benchmarks
This directory contains benchmarks for tracking RQRCode performance over time, measuring both **end-to-end workflows** (generation + export) and **rendering-only** performance.
## Quick Start
```bash
# Install dependencies
bundle install
# Run specific format benchmarks
rake benchmark:svg
rake benchmark:png
rake benchmark:html
rake benchmark:ansi
rake benchmark:format_comparison
# Run all benchmarks (takes ~1-2 minutes)
rake benchmark:all
```
## Results Storage
**All benchmark results are automatically saved to `benchmark/results/` as JSON files.**
Each run creates timestamped files with the format:
- `ips_<benchmark_name>_YYYYMMDD_HHMMSS.json` - Performance data (iterations/sec, comparisons)
- `memory_<benchmark_name>_YYYYMMDD_HHMMSS.json` - Memory allocation data
This allows you to:
- Track performance changes over time
- Compare before/after optimisation results
- Share baseline results with the team
- Generate summary reports comparing different runs
**Note:** The `results/` directory is gitignored - results are local to your machine.
## Benchmark Types
All benchmarks run **two modes**:
### 1. End-to-end (PRIMARY METRIC)
Measures the complete user workflow: `RQRCode::QRCode.new(data).as_svg`
- **What it shows**: Total time users experience, including rqrcode_core generation
- **Why it matters**: This is what users actually do - reflects real-world performance
- **When to use**: Track overall improvements, compare formats for actual usage
- **File naming**: `ips_e2e_*_YYYYMMDD_HHMMSS.json`
### 2. Rendering-only (DIAGNOSTIC METRIC)
Measures only export performance using pre-generated QR codes
- **What it shows**: Export format performance in isolation
- **Why it matters**: Helps identify which export method needs optimisation
- **When to use**: When optimising export code, isolating rendering bottlenecks
- **File naming**: `ips_*_YYYYMMDD_HHMMSS.json`
**Key Insight**: End-to-end benchmarks often show QR generation is the bottleneck (all formats perform similarly), while rendering-only benchmarks reveal differences between export formats (SVG is ~4x slower than HTML due to algorithmic complexity).
## Available Benchmarks
Each benchmark tests 3 QR code sizes (small, medium, large) with realistic data and runs both end-to-end and rendering-only modes:
### Format Comparison (`benchmark/format_comparison.rb`)
Compares all export formats head-to-head using a medium-sized QR code.
- **End-to-end metric**: Which format is fastest for users? (Usually ~same due to generation overhead)
- **Rendering-only metric**: Which export format is most efficient?
### SVG Export (`benchmark/svg_export.rb`)
Tests SVG path mode (most common use case) across different QR sizes.
- **End-to-end metric**: Total time for generation + SVG export
- **Rendering-only metric**: SVG rendering performance in isolation
### PNG Export (`benchmark/png_export.rb`)
Tests PNG with default sizing (most common use case) across different QR sizes.
- **End-to-end metric**: Total time for generation + PNG export
- **Rendering-only metric**: PNG rendering performance in isolation
### HTML Export (`benchmark/html_export.rb`)
Tests HTML table export across different QR sizes.
- **End-to-end metric**: Total time for generation + HTML export
- **Rendering-only metric**: HTML rendering performance in isolation
### ANSI Export (`benchmark/ansi_export.rb`)
Tests ANSI terminal output across different QR sizes.
- **End-to-end metric**: Total time for generation + ANSI export
- **Rendering-only metric**: ANSI rendering performance in isolation
## Understanding the JSON Output
### End-to-end Results
Files named `ips_e2e_*` contain generation + export times:
```json
{
"label": "All Export Formats (end-to-end)",
"timestamp": "2025-12-17T21:46:51+00:00",
"ruby_version": "3.3.4",
"results": {
"svg": {
"iterations_per_second": 16.71,
"standard_deviation": 0.00,
"samples": 84,
"comparison": 1.09
},
"ansi": {
"iterations_per_second": 18.13,
"standard_deviation": 5.50,
"samples": 91,
"comparison": 1.0
}
}
}
```
### Rendering-only Results
Files named `ips_*` (without `e2e`) contain export-only times:
```json
{
"label": "All Export Formats",
"timestamp": "2025-12-09T19:49:35+00:00",
"ruby_version": "3.3.4",
"results": {
"svg": {
"iterations_per_second": 186.71,
"standard_deviation": 0.54,
"samples": 18,
"comparison": 7.28
},
"ansi": {
"iterations_per_second": 1359.48,
"standard_deviation": 0.22,
"samples": 135,
"comparison": 1.0
}
}
}
```
**Common fields:**
- `iterations_per_second`: Higher is better (more iterations completed per second)
- `standard_deviation`: Lower is more consistent (percent variation)
- `comparison`: Multiplier vs fastest (1.0x = fastest, higher = slower)
- `samples`: Number of iterations run for measurement
## Test Data
Benchmarks use 3 representative QR code sizes:
- **small**: GitHub URL (~40 chars, typical use case)
- **medium**: Lorem ipsum sentence (~100 chars)
- **large**: 500 characters (stress test)
## Interpreting Results
### What to track over time:
1. **End-to-end i/s**: Are users getting faster overall? (Includes rqrcode_core improvements)
2. **Rendering-only i/s**: Are export methods getting more efficient?
3. **Relative comparisons**: How do formats compare to each other in both modes?
4. **Memory allocations**: Are we creating fewer objects?
### Interpreting Performance Changes
**When end-to-end improves but rendering-only doesn't:**
- Improvements came from rqrcode_core (QR generation algorithm)
- Export methods haven't changed
**When rendering-only improves but end-to-end shows modest gains:**
- Export methods got faster, but generation time dominates
- For small improvements, generation overhead masks rendering gains
**When both improve proportionally:**
- Changes benefited the whole pipeline
## Latest Benchmark Results
**Last Updated: 2026-01-08 14:09:06 UTC**
**Ruby Version: 3.3.4**
**Platform: Apple M-series**
**rqrcode_core version: 2.0.1**
### Quick Reference Baselines
#### End-to-end (Generation + Export) - Medium QR Code
*User-facing performance - what matters for real-world usage*
| Format | Iterations/sec | Std Dev | Samples | Slowdown vs Fastest |
|--------|----------------|---------|---------|---------------------|
| HTML | 34.1 | 2.90% | 171 | 1.00x (baseline) |
| ANSI | 34.1 | 0.00% | 171 | 1.00x (same-ish) |
| PNG | 33.6 | 0.00% | 171 | 1.01x (same-ish) |
| SVG | 32.2 | 0.00% | 162 | 1.06x (same-ish) |
**Key Insight**: All formats now perform very similarly (~32-34 i/s) because QR generation dominates the time. SVG optimisations brought it in line with other formats for end-to-end usage.
#### Rendering-only (Export Performance) - Medium QR Code
*Diagnostic metric - shows export efficiency in isolation*
| Format | Iterations/sec | Std Dev | Samples | Slowdown vs Fastest |
|--------|----------------|---------|---------|---------------------|
| HTML | 1,876 | 0.70% | 9,464 | 1.00x (baseline) |
| ANSI | 1,310 | 6.20% | 6,615 | 1.43x |
| PNG | 840 | 4.90% | 4,214 | 2.23x |
| SVG | 424 | 1.70% | 2,150 | 4.42x |
**Key Insight**: SVG rendering improved from 184 i/s to 424 i/s (+130%) after optimisations. The gap vs HTML reduced from 10x to 4.4x. Remaining gap is due to algorithmic complexity (edge detection + path tracing vs simple iteration).
### Performance by QR Code Size
*Note: Higher iterations/sec is better; lower std dev is better; lower slowdown is better*
#### SVG Export (End-to-end)
| Size | Iterations/sec | Std Dev | Slowdown vs Small |
|--------|----------------|---------|-------------------|
| Small | 102.7 | 1.00% | 1.00x (baseline) |
| Medium | 32.2 | 0.00% | 3.19x |
| Large | 10.9 | 0.00% | 9.41x |
#### PNG Export (End-to-end)
| Size | Iterations/sec | Std Dev | Slowdown vs Small |
|--------|----------------|---------|-------------------|
| Small | 102.5 | 1.00% | 1.00x (baseline) |
| Medium | 33.6 | 0.00% | 3.05x |
| Large | 11.4 | 0.00% | 9.00x |
#### HTML Export (End-to-end)
| Size | Iterations/sec | Std Dev | Slowdown vs Small |
|--------|----------------|---------|-------------------|
| Small | 109.2 | 3.70% | 1.00x (baseline) |
| Medium | 34.2 | 0.00% | 3.19x |
| Large | 11.6 | 0.00% | 9.40x |
#### ANSI Export (End-to-end)
| Size | Iterations/sec | Std Dev | Slowdown vs Small |
|--------|----------------|---------|-------------------|
| Small | 108.6 | 0.90% | 1.00x (baseline) |
| Medium | 33.9 | 0.00% | 3.21x |
| Large | 11.5 | 0.00% | 9.42x |
### Memory Allocations
*Note: Lower is better for both metrics*
| Format | Total Objects Allocated | Total Memory (MB) |
|--------|-------------------------|-------------------|
| HTML | 451 | 18.0 |
| PNG | 357,676 | 23.1 |
| SVG | 2,157,951 | 113.8 |
**Key Insights:**
- **End-to-end**: All formats now perform similarly (~32-34 i/s) - SVG optimisations closed the gap
- **Rendering-only**: HTML is fastest (1,876 i/s), SVG improved significantly (424 i/s, was 184 i/s)
- **SVG improvements**: +130% rendering speed, 71% memory reduction after 2026-01-08 optimisations
- **Optimisation priority**: Improvements to rqrcode_core have biggest impact on user experience
- **Format choice**: For high-volume rendering, HTML/ANSI are still faster but SVG is now competitive
- All formats show 3-10x performance degradation as QR size increases
- Memory usage: HTML uses 6x less memory than SVG (was 22x before optimisation)
## Notes
- **Primary metric**: End-to-end results reflect real user experience
- **Secondary metric**: Rendering-only helps diagnose where to optimise
- Focus on relative comparisons, not absolute numbers
- Results vary by system (CPU, Ruby version, rqrcode_core version)
- Run benchmarks before and after making changes to measure impact
- Full suite runs in ~2-3 minutes (doubled due to two modes per benchmark)
- When rqrcode_core updates, expect end-to-end improvements even without changes to this gem
|