SVG QRCode 生成性能基准测试
测试概述
本基准测试对比了以下 Rust QRCode 生成库的 SVG 生成性能:
⚠️ 本页数据来自 SVG 专项 harness(
benchmark_svg_rust.json),不是全仓库的权威口径。 权威数据是benchmark_speedup_verified.json(端到端 SVG,qrcode-fast比 kennytm 快 9-19 倍、qrcode-rust快 6-9 倍)。两套数字口径不同,请勿混用,详见下方「性能对比」的警告。
最新测试结果
数据文件 benchmark_svg_rust.json 生成于: 2026-02-03
测试环境
- 平台: Windows x64
- Rust: rustc 1.89.0
- 数据文件生成时间: 2026-02-03
- 验证工具: resvg + rqrr
测试用例
性能对比
数据来源:benchmark_svg_rust.json(SVG-only harness,timestamp: 2026-02-03),
取该文件 test_cases[*].*_time.avg_time_us 的 avg_time_us 字段。
⚠️ 口径警告:本表与 后端 PK 对比 中的权威表格(fast 18.5-64.4 µs、rust 32.1-130.2 µs、kennytm 210.4-826.2 µs, 比率 9-19x / 6-9x)不是同一套测量。本表来自较早的 SVG 专项 harness,热身与计时方式不同, 且
benchmark_svg_rust.json中部分用例标记为valid: false(见下方「验证状态」)。 选型请以benchmark_speedup_verified.json的 9-19x / 6-9x 为准,本表仅作历史参考。
验证状态
以下状态直接取自 benchmark_svg_rust.json 各用例的 valid 字段:
说明:
- ✅ 表示该用例生成的二维码可以通过标准扫描器正确解码(JSON 中
valid: true) - ❌ 表示该用例未能通过解码验证(JSON 中
valid: false)。该文件记录的是修复前的状态, 当前版本请以benchmark_speedup_verified.json的四用例验证结果为准(其correctness_check为 resvg + rqrr) - 本页下方「历史问题(已修复)」描述的正是这批
valid: false的成因
📌 历史版本这里写的是「所有包在所有测试用例上均验证通过 ✅」, 与
benchmark_svg_rust.json的实际字段不符(Complex / Long 两例为false),已按原始数据更正。
⚠️ 这两张表不得与
benchmark_speedup_verified.json的数值混合使用: 两者是不同 harness、不同口径的测量。全仓库的性能结论一律以后者为准。
主要发现
🏆 性能冠军
@veaba/qrcode-fast 在两个数据源中都是最快的实现:
- 权威口径(
benchmark_speedup_verified.json,端到端 SVG):比kennytm-qrcode快 9-19 倍,比@veaba/qrcode-rust快 1.5-2.2 倍 - 本页 SVG 专项口径(
benchmark_svg_rust.json):比kennytm-qrcode快 19.8-22.4 倍,比@veaba/qrcode-rust快约 2.3-2.6 倍
两套数字的差异来自热身方式、计时范围与输入文本不同,不可混用。 对外表述与选型请使用权威口径的 9-19 倍 / 6-9 倍。
✅ 验证状态
本页数据文件(benchmark_svg_rust.json)中的状态并非全部通过:Simple 与 URL 两例有效,
Complex 与 Long 两例的 qrcode-rust / qrcode-fast 均标记为 valid: false(详见上方「验证状态」表)。
当前版本的正确性验证结果以 benchmark_speedup_verified.json 为准 —— 其四个用例(11 / 17 / 31 / 74 字节)
经 resvg + rqrr 独立解码均能还原原文。
kennytm-qrcode: 社区成熟实现,稳定可靠qrcode-rust: 功能完整,与 kennytm 参考实现保持一致;⚠️ 约 60 字节以上存在解码失败问题qrcode-fast: 极致性能优化,功能完整;⚠️ 约 110 字节以上存在解码失败问题
📊 SVG 文件大小
数据来源:benchmark_svg_rust.json 的 svg_size 字段(单位:字节)。
qrcode-fast 的 SVG 体积最小且接近 kennytm;qrcode-rust 的输出明显更大(约为 qrcode-fast 的 3.3-3.8 倍)。
历史版本此表写的是 "kennytm ~4.2 KB / qrcode-rust ~4.3 KB / qrcode-fast ~4.1 KB" 之类的近似值, 把
qrcode-rust的 13.8 KB 低估成了 4.3 KB,与 JSON 不符,已按原始字段更正。
五方对比(Node / Bun / Rust,统一纠错级别 M)
📌 本节含两套数据,请注意区分:
- 下方交互式图表与原始数据文件(M 级,五方) —— 数据已落盘为
benchmark_svg_five_way.json(_status: secondary-source), 由node bench/svg-benchmark/collect-five-way.mjs采集三个 harness 生成。- Rust 三方的三个新用例已登记进权威文件
benchmark_speedup_verified.json(用例名HelloWorld/SiteUrl/CjkLong,H 级,与权威文件原有四例同一口径)。 因此权威文件的极值已由旧值11-19x拉宽为9-19x、1.7-2.2x拉宽为1.5-2.2x(旧区间已作废)。
为什么要先统一纠错级别
直接对比五方会得到不公平的结果:@veaba/qrcode-fast / @veaba/qrcode-rust 默认使用 H 级(30% 纠错),
而 kennytm-qrcode 默认使用 M 级(15% 纠错)。同一段 hello world,veaba 系产出 25×25 模块,
kennytm 仅 21×21 —— 相当于让 kennytm 做更小的活。
因此本节统一到纠错级别 M。实测确认五方产物模块数完全一致(21 / 25 / 45),差异才可归因于实现效率。
测试用例
交互式性能对比(单位:µs,预热 20 次 + 测量 100 次,取平均)
上框是原始数据文件
benchmark_svg_five_way.json。 下方图表直接读取同一文件渲染,因此不会出现「文档手抄数字与 JSON 不一致」的情况。
排序(按 115 字节长文本):qrcode-fast < qrcode-rust < qrcode-bun < kennytm-qrcode < qrcode-node
已登记进权威文件的三个用例(H 级)
上表是 M 级的横向对比。另有三个用例以 H 级(与权威文件原有四例同一口径)
登记进 benchmark_speedup_verified.json:
该三例的
speedup极值(fast/kennytm 最低 9.3、fast/rust 最低 1.47)拉宽了权威文件的全量区间, 故headline已由11-19x/1.7-2.2x更新为9-19x/1.5-2.2x,旧区间登记进deprecated_claims。
🔍 SVG 落盘(供人工扫码验证)
生成上述用例时,每个实现前 5 次生成的 SVG 会同部落盘,便于人工用手机扫描核对:
例如 fast_long_0.svg = qrcode-fast 生成的长文本二维码第 1 次。
⚠️ 该目录已在
.gitignore中,不会入库,属本地人工验证产物。fast_*与rust_*内容逐字节相同(同源编码器qrcode-rust-shared),这是预期结果。
自动校验这些 SVG(推荐,替代人工逐个扫码)
该工具按文件名解析出「实现 + 用例」,用 resvg 渲染 + rqrr 解码还原文本并与原始输入比对,
覆盖全部五方(validate-all 仅覆盖 Rust 三方)。全部通过退出码 0,任一失败退出码 1。
目录不存在时优雅跳过(该目录不入库,他人 clone 后本就没有)。
注意直接调用
cargo run时要带--features validation,否则解码函数会返回「未启用验证特性」。 走pnpm run bench:svg:validate则已包含该参数。
✅ 已修复缺陷:两个实现曾产出不可解码的二维码
validate-svg-dir 首次运行即发现真实缺陷(用独立解码器 resvg + rqrr 确认),
现已全部修复:
缺陷 1 — qrcode-bun 缺版本信息(v7+ 不可解码)
- 边界:58 字节(v6)通过,59 字节(v7)失败;
qrcode-node同版本通过,故缺陷在 bun 自身。 - 根因:
packages/qrcode-bun/src/index.ts的setupTypeNumber()是空实现(仅有// 简化版本注释), v7+ 缺少 18 位版本信息,解码器无法判定版本。 - 修复:实现 BCH(18,6) 版本信息写入(生成多项式
G18 = 0x1F25), 右上 / 左下各写一份,与qrcode-js-shared/qrcode-fast的既有实现一致。
缺陷 2 — 对齐图案坐标表从 v22 起整体错位(影响 node 与 bun)
- 现象:
qrcode-node在 v22+ 不可解码(440 字节起失败),qrcode-bun在 v23+ 失败。 - 根因:
PATTERN_POSITION_TABLE含 41 条记录(应为 40), 多出一条重复的[6, 28, 50, 72, 98],导致 v22 及以后每个版本都取到下一个版本的坐标。 表内 v40 实为 v39 的值,v40 真实坐标缺失。 - 修复:删除重复行,并用 QR 标准(ISO 18004 Annex E)坐标校正 v22–v40。 现 40 个版本与标准逐项吻合。
- 该表在
qrcode-js-shared与qrcode-bun各有一份副本,两处都已修复。
修复后验证
另经逐版本扫描(v7 → v40,59 → 1273 字节):node 与 bun 24/24 全部通过,
覆盖到 QR 标准 H 级上限 1273 字节。
回归测试已固化于
bench/rust-tools/tests/qr_validity.rs::persisted_svgs_all_decode, 五方 × 三用例全量断言,不再豁免任何实现。
⚠️ 口径限制(请勿横向解读本表)
本表不是「运行时优劣」排行榜:
- Rust 三方同进程可比,可信度最高。
qrcode-node与qrcode-bun是两套不同实现 —— 前者封装@veaba/qrcode-js-shared的QRCodeCore, 后者自带独立实现。因此qrcode-bun快于qrcode-node主要反映实现差异,不能归因于 Bun 运行时更快。- 跨语言(JS vs Rust)的倍数不予提供:不同进程、不同 GC/JIT、不同计时原语
(
performance.now()vsInstant::now()),相除得到的倍数没有意义。
复现命令
一条命令即可跑完三个 harness 并写入数据文件(推荐):
也可单独运行每个 harness:
采集器会校验各实现在同一用例下的模块数是否一致;不一致说明纠错级别没对齐, 它会以退出码 1 报告,此时跨实现的比较无效。
未安装 Bun 时可加
--skip-bun(图表会自动隐藏该行);想复用已编译的 Rust 产物可加--skip-rust。不带
--ec=M/ 使用--bin five-way-rust可复现「未对齐纠错级别」的原始结果, 用于观察默认级别差异带来的产物规模变化。
生成权威口径数据 + SVG 落盘(H 级)
推荐:一条命令跑完「五方生成 + 独立解码校验」
校验全部通过退出码 0;任一 SVG 无法解码则退出码 1(便于接入 CI)。 只做校验(不重新生成):
分步执行(需要单独跑某一方时)
⚠️ Node 侧按路径导入
packages/qrcode-node/dist/index.js。 若改动了qrcode-js-shared,需先重新构建,否则测到的仍是旧产物:pnpm --filter @veaba/qrcode-js-shared build && pnpm --filter @veaba/qrcode-node build
运行基准测试
快速测试(10次运行)
完整测试(100次运行)
命令说明
结果文件
测试完成后,结果会保存到以下位置:
- SVG 文件:
docs/public/bench/benchmark-output/*.svg - 五方数据(本页图表来源):
benchmark_svg_five_way.json(由pnpm bench:svg:five-way生成,含 fast / rust / kennytm / node / bun) - Rust 三方专项 JSON:
benchmark_svg_rust.json(旧口径) - 权威口径数据:
benchmark_speedup_verified.json(由cd bench/rust-tools && cargo run --release --bin verify-speedup生成)
查看详细结果
以下为 SVG 专项 harness 的原始 JSON(含 valid 字段,可自行核对上方「验证状态」表):
使用建议
按场景推荐
版本选择建议
✅ 两个 Rust 包现已覆盖全部 40 个版本,H 级最大 1273 字节(QR 标准上限),与 JS 实现一致;
此前的解码边界问题(qrcode-rust 约 60 字节、qrcode-fast 约 110 字节)均已修复,
详见 benchmark_speedup_verified.json 的 capability 与 fixed_defects。
纠错级别建议
所有包在所有纠错级别(L/M/Q/H)枚举上均可用,但请注意:本轮没有重新测量按纠错级别分组的性能,
权威数据文件 benchmark_speedup_verified.json 的四个用例均使用默认的 H 级别。
本页历史版本给出的 L/M/Q/H 性能表已删除(来源不可复现)。
历史问题(已修复)
2026-02-02 之前的验证失败问题
在早期版本中,@veaba/qrcode-rust 和 @veaba/qrcode-fast 在某些复杂文本上验证失败,错误类型为 DataEcc(Reed-Solomon 纠错码错误)。
问题原因
- 格式信息放置错误:水平和垂直格式信息的位置不正确
- Mask Pattern 应用错误:mask pattern 的公式应用有误
修复内容
-
格式信息放置修复
- 垂直方向(第8列):从上到下正确放置
- 水平方向(第8行):从右到左正确放置
- 正确处理 test 模式
-
Mask Pattern 修复
- 统一使用 mask pattern 0:
(row + col) % 2 == 0 - 确保与 kennytm 的默认选择一致
- 统一使用 mask pattern 0:
修复验证
以下结论来自 benchmark_speedup_verified.json(权威口径,四个用例经 resvg + rqrr 解码验证):
- ✅ "Hello World" (11 B):解码还原成功
- ✅ "Test QR Code 123" (17 B):解码还原成功
- ✅ URL (31 B) 与 Long (74 B):解码还原成功
✅ 现已彻底修复:
qrcode-fast与qrcode-rust均覆盖全部 40 个版本(H 级最大 1273 字节),
调试工具
对比模块模式
调试二维码生成
验证生成的 SVG
调试格式信息
调试数据编码
数据来源: benchmark_svg_rust.json(本页表格) | benchmark_speedup_verified.json(权威口径)
复现权威口径: cd bench/rust-tools && cargo run --release --bin verify-speedup
最后更新: 2026-03-20(更正验证状态与文件大小,标注双口径来源,撤回 15-25 倍 / 8-10 倍表述)