Yew 基准测试结果处理器 process-benchmark-results 原理与 CI 实战
【免费下载链接】yewRust / Wasm framework for creating reliable and efficient web applications项目地址: https://gitcode.com/gh_mirrors/ye/yew
导读
process-benchmark-results 是 Yew 仓库(Rust/Wasm 框架)中一个轻量但关键的工具:它将 js-framework-benchmark 与 CI 工作流,完整讲解数据格式差异、转换算法、命令行用法以及它在 Yew 性能监控流水线中的实际位置。读完本文,你将能够复现 Yew 的基准结果转换流程,并理解如何在自己的 Rust/Wasm 项目中接入持续基准测试。
一、工具定位:两套基准数据格式之间的桥梁
关联文档 tools/process-benchmark-results/README.md 用一句话概括了工具的全部使命:
Processes benchmark results —— From array of js-framework-benchmark —— Into array that works for the continuous-benchmark GitHub Action
也就是说,该工具解决的是数据格式适配问题,而非性能数据的采集或计算本身:
- 上游输入:js-framework-benchmark 的
webdriver-ts驱动浏览器跑分后,每个基准(benchmark)会输出独立的 JSON 结果文件; - 下游输出:
benchmark-action/github-action-benchmark@v1期望一个扁平的 JSON 数组,每个元素形如{ "name": ..., "unit": ..., "value": ... }。
从仓库结构看,这个工具与 benchmark-struct(基于结构组件 API 的基准应用)、benchmark-hooks(基于函数组件/Hooks 的基准应用)以及 benchmark-core(虚拟 DOM 微基准)共同构成 Yew 的性能测试工具链,而 process-benchmark-results 处于这条链路的"出口"位置。
二、输入格式:js-framework-benchmark 的结果结构
Yew 的 CI 通过webdriver-ts运行基准,生成的原始文件位于js-framework-benchmark/webdriver-ts/results/*.json。为了把它们合并成一个数组,benchmark.yml 中使用了jq -s(slurp 模式):
jq -s . js-framework-benchmark/webdriver-ts/results/*.json \ | cargo run --manifest-path yew/Cargo.toml --release -p process-benchmark-results \ > artifacts/results.json每个原始结果文件的 JSON 结构对应源码中的反序列化模型(main.rs 第 24-29 行):
struct JsKrauseBenchmarkResult<'r> { framework: &'r str, // 例如 "keyed/yew" benchmark: &'r str, // 例如 "01_run1k" r#type: &'r str, // 例如 "cpu" 或 "memory" values: HashMap<&'r str, ResultData>, // 键如 "total"/"DEFAULT",值为统计对象 } struct ResultData { median: Value, // 中位数,必须是数值 // 某些键可能缺失 }值得注意的是ResultData只声明了median一个字段,注释明确写着 "some keys missing"——即原始数据中还存在min、max、mean、std_dev、geomean等其他统计量,但该工具只关心中位数(median),这正是它输出给图表 Action 的单一数值指标。这种"按需声明字段"的做法,正是 serde 默认允许缺失字段的宽容反序列化策略。
三、转换核心算法:median 提取与类型分流
transform_results 函数 实现了核心转换逻辑,其关键设计是按基准类型选择不同的 values 键:
fn transform_results(mut result: JsKrauseBenchmarkResult<'_>) -> GhActionBenchmark { let key = if result.r#type == "cpu" { "total" // CPU 基准:取 "total" 键 } else { "DEFAULT" // 其他类型(如内存):取 "DEFAULT" 键 }; let values = result.values.remove(key).unwrap_or_else(|| { panic!( "Expect benchmark data to be present for type {}. Found keys: {:?}, expected {key:?}", result.r#type, result.values.keys().cloned().collect::<Vec<_>>(), ) }); assert!( values.median.is_number(), "expected a numerical benchmark value" ); GhActionBenchmark { name: format!("{} {}", result.framework, result.benchmark).replace('"', ""), unit: String::default(), value: values.median, } }转换规则可总结为一张表:
| 输入字段 | 处理逻辑 | 输出字段 |
|---|---|---|
r#type == "cpu" | 从values中取出键为total的统计对象 | 取其median作为value |
r#type为其他值(如 memory) | 从values中取出键为DEFAULT的统计对象 | 取其median作为value |
framework+benchmark | 拼接为"{framework} {benchmark}",并移除字符串中的双引号" | 作为name |
| — | 固定为空字符串 | unit |
| — | 数值型中位数 | value(保持serde_json::Value原样) |
这里有几个值得关注的工程细节:
- panic 式的防御:当某个基准类型对应的键不存在时,工具直接
panic!并打印出当前实际存在的键集合,帮助 CI 日志快速定位"数据缺失"问题;assert!则保证了输出给图表的数据一定是数值,避免非法类型混入。 remove而非get:HashMap::remove在取出值的同时移除了该键,避免借用冲突,同时利用unwrap_or_else惰性构造错误信息。Value类型的贯穿使用:median以serde_json::Value存储,输出时原样序列化,既不需要预先确定数值精度,也与下游 Action 对value的宽松要求兼容。- 名称清洗:
.replace('"', "")防止框架名或基准名中的引号破坏下游 JSON 字段或图表标题。
四、输出格式:continuous-benchmark Action 的输入契约
转换后的数据结构定义在 main.rs 第 9-14 行:
#[derive(Serialize)] struct GhActionBenchmark { name: String, unit: String, value: Value, }即一个GhActionBenchmark数组,例如:
[ { "name": "keyed/yew 01_run1k", "unit": "", "value": 123.456 }, { "name": "keyed/yew-hooks 02_replace1k", "unit": "", "value": 98.7 } ]这个结构正是benchmark-action/github-action-benchmark@v1的customSmallerIsBetter自定义工具模式所要求的输入:name用于图表与告警的标识,value为单一数值(此处为中位数),unit留空由 Action 侧自行决定展示单位。
五、在 Yew CI 流水线中的完整闭环
process-benchmark-results 只是"Benchmark"工作流的一环,理解它需要看它在整条流水线中的上下游(benchmark.yml):
- 基准构建:分别用
wasm-pack build --release --target web构建 benchmark-struct 与 benchmark-hooks,输出到 js-framework-benchmark 仓库约定的bundled-dist/目录; - 跑分:启动 js-framework-benchmark 服务器,用
xvfb-run npm run bench -- --framework keyed/yew keyed/yew-hooks --runner playwright驱动浏览器执行基准,产出results/*.json; - 格式转换(本文主角):
jq -s .合并所有结果文件后,通过cargo run --manifest-path yew/Cargo.toml --release -p process-benchmark-results执行转换,将输出重定向到artifacts/results.json;同时把完整事件信息写入artifacts/.PR_INFO(用于区分 master 与 PR 场景); - 上传产物:
actions/upload-artifact@v7将artifacts/上传为名为results的构建产物(保留 1 天)。
随后由 post-benchmark.yml 在"Benchmark"工作流完成后触发:下载results产物,交给benchmark-action/github-action-benchmark@v1处理——配置了tool: "customSmallerIsBetter"、output-file-path: artifacts/results.json、alert-threshold: "200%"(相对基线劣化超过 200% 时告警并 @ yewstack 团队),并在 master 提交上自动推送数据到gh-pages分支形成趋势图表,在 PR 场景则只生成对比评论(auto-push与save-data-file根据.PR_INFO中是否存在 PR 编号动态决定)。
六、工具本身的工程属性
从 Cargo.toml 可以确认该工具的轻量定位:
- 版本与语法:
version = "0.1.0",edition = "2024",rust-version继承工作区(根 Cargo.toml 声明为 1.85); - 依赖极少:仅
anyhow(统一错误处理)、serde(含 derive,序列化/反序列化)、serde_json(JSON 解析与 Value 类型); - 无异步、无网络、无文件系统依赖:整个程序通过标准输入读取、标准输出写出,是一个纯粹的"管道过滤器"(pipe filter);
- 工作区归属:根 Cargo.toml 将
tools/*纳入 workspace members,因此 CI 中可用cargo run -p process-benchmark-results直接运行;同时 Makefile.toml 覆盖了doc-test任务(该工具不包含文档测试,输出 "No doctests run for process-benchmark-results"),说明它不参与文档测试流水线。
七、实战:在任意 Rust 项目中复现该转换
即便不依赖 Yew 的 CI 配置,你也可以独立复用这条转换链路。假设你的 js-framework-benchmark 结果位于results/目录,只需两步:
# 1. 合并多个基准结果文件为单个 JSON 数组 jq -s . results/*.json > merged.json # 2. 通过 stdin 喂给转换工具,从 stdout 取得 Action 兼容格式 cargo run --release -p process-benchmark-results < merged.json > results.json注意在仓库内运行时应通过工作区定位(--manifest-path Cargo.toml),否则 cargo 无法解析process-benchmark-results包。转换完成后的results.json可直接作为benchmark-action/github-action-benchmark@v1的customSmallerIsBetter数据源。
八、小结与延伸阅读
process-benchmark-results 用约 70 行 Rust 代码,精准完成了"js-framework-benchmark 原始结果 → continuous-benchmark Action 输入"的格式适配,其按type分流键名(total/DEFAULT)、取中位数、清洗名称并 panic 式校验的设计,保证了 Yew 持续基准图表数据的稳定与可诊断性。它是 Yew 将第三方基准框架与 GitHub Actions 图表能力衔接起来的关键一环。
若想进一步探索,可从以下仓库路径继续深入:
- 转换逻辑完整源码:tools/process-benchmark-results/src/main.rs
- 触发与运行该工具的工作流:.github/workflows/benchmark.yml
- 消费转换结果并发布图表/告警的工作流:.github/workflows/post-benchmark.yml
- 被基准的 Yew 应用:tools/benchmark-struct、tools/benchmark-hooks
- SSR 基准的配套处理脚本(用于生成 PR 评论表格):ci/make_benchmark_ssr_cmt.py
【免费下载链接】yewRust / Wasm framework for creating reliable and efficient web applications项目地址: https://gitcode.com/gh_mirrors/ye/yew
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考