Deno 基准测试体系:deno_bench 基准套件的运行、过滤与结果产出全解析
本文围绕 Deno 仓库 tests/bench/README.md 所讲的两个核心点——如何用 cargo bench --bench deno_bench -- <名称> 对基准测试进行过滤运行,以及基准结果如何被产出与可视化——展开,并结合 tests/bench/main.rs 的完整实现,说明各指标(执行时间、内存峰值、二进制体积、系统调用数等)的采集方式与结果 JSON 的聚合流程。读完后你可以独立跑通 Deno 的官方基准套件、理解每个过滤参数对应哪一组测量,并知道 target/bench.json 中各字段的真实来源。
1. 基准套件入口:deno_bench 目标与过滤命令
tests/bench/README.md 给出的第一条实战命令就是基准过滤的入口:
cargo bench --bench deno_bench -- bundle
其中 -- 之后的参数(README 中示例为 bundle)是基准名称过滤器。这个 deno_bench 基准目标在 tests/bench/Cargo.toml 中声明:
[package]
name = "bench_tests"
autobenches = false
[[bench]]
name = "deno_bench"
harness = false
path = "main.rs"
[[bench]]
name = "lsp_bench_standalone"
harness = false
path = "lsp_bench_standalone.rs"
几个关键点:
harness = false表示该目标不依赖 libtest 默认测试框架,main.rs是完全自托管的执行器;- 该包被列入了根 Cargo.toml 的 workspace 成员(
"tests/bench"、"tests/bench_util"),且根工作区以deno_bench_util = { version = "0.252.0", path = "./tests/bench_util" }的形式导出了基准工具库; - 除
deno_bench外还有一个独立的lsp_bench_standalone目标,专门用于 LSP 基准的独立运行。
tests/bench/main.rs#L363-L384 中的过滤逻辑与 README 的命令一一对应:程序启动时读取第一个位置参数,若它是某个基准名则 benchmarks.retain(|s| s == &filter) 只保留该组;程序同时要求命令行中出现 --bench 开关(cargo 在基准模式下传给自定义 harness 的约定参数),否则直接退出,避免误当作普通二进制执行。
需要说明的一点是:README 中的 bundle 是历史基准名(对应早期对 deno bundle 子命令的计时)。从当前源码的基准清单看,现存的过滤值已变为下面第 2 节列出的六组,README 示例更多是演示“如何传过滤参数”这一机制本身。
2. 可用的过滤参数:六组基准
tests/bench/main.rs#L363-L370 中定义了当前支持的基准组清单:
| 过滤参数 | 测量对象 | 实现位置 |
|---|---|---|
exec_time |
典型命令(deno run 等)的执行耗时分布 |
main.rs#L151-L218 |
binary_size |
deno 可执行文件、swc/v8 rlib、快照文件体积 | main.rs#L247-L293 |
cargo_deps |
Cargo.lock 中的依赖包总数 |
main.rs#L321-L334 |
lsp |
LSP 请求回放(补全、悬停、编辑)耗时 | lsp.rs |
strace |
系统调用总数、线程数(仅 Linux) | main.rs#L432-L476 |
mem_usage |
峰值内存(time -v 采样) |
main.rs#L295-L319 |
例如只跑 LSP 一组:cargo bench --bench deno_bench -- lsp;不带过滤参数则六组全部执行。
3. exec_time:hyperfine 驱动的执行时间基准
这是套件中最核心的一组。tests/bench/main.rs#L34-L147 以 EXEC_TIME_BENCHMARKS 常量表声明了全部被测命令,每一项是 (名称, deno 参数, 期望退出码) 三元组,覆盖的场景包括:
- 冷启动:
cold_hello、cold_relative_import(带--reload强制重新加载模块图),以及对应热路径的hello、relative_import; - 错误路径:
error_001,并断言进程退出码为 1; - Worker 相关:
workers_startup、workers_round_robin、workers_large_message; - 内置功能热点:
text_decoder、text_encoder、text_encoder_into、response_string。
执行细节(main.rs#L151-L218):
- 使用预编译好的
hyperfine工具(test_util::prebuilt_tool_path("hyperfine"))一次性跑完所有命令,参数为--export-json hyperfine_results.json --warmup 3,即每组命令 3 次预热; - 源码注释明确要求
cold_*用例必须排在热用例之前——因为冷启动基准顺带完成了模块缓存的填充,先执行才能保证后续热用例读到的缓存状态正确; - 对
error_001这类带期望退出码的用例,命令尾部会追加; test $? -eq 1的 shell 断言,防止“命令跑挂了但耗时很短”污染数据; - 从 hyperfine 的 JSON 输出中只保留
RESULT_KEYS = ["mean", "stddev", "user", "system", "min", "max"]六个统计量,按基准名归入结果字典。
4. mem_usage、strace、binary_size 与 cargo_deps
内存峰值(mem_usage):run_max_mem_benchmark 对每个 EXEC_TIME_BENCHMARKS 用例执行 time -v <deno> <args>,解析 stderr 中的最大驻留内存。这意味着该组指标依赖 Linux 的 time 命令,非 Linux 环境需自行保证可用。
系统调用与线程数(strace):仅在 cfg!(target_os = "linux") 下启用。以 strace -c -f -o <临时文件> 包裹每条被测命令,并强制 LC_NUMERIC=C 保证数字格式可解析;线程数取 clone/clone3 调用数加 1,系统调用总数取 total(main.rs#L432-L476)。
二进制体积(binary_size):get_binary_sizes 汇总四类体积——deno 可执行文件本身、target/release/deps 下所有 libswc* 与 libv8* rlib 的累计大小,以及三个编译期快照文件 CLI_SNAPSHOT.bin、RUNTIME_SNAPSHOT.bin、COMPILER_SNAPSHOT.bin。由于 cargo 的 OUT_DIR 不可预测,快照文件通过 walkdir 全树搜索取得,且同名文件以 mtime 最新者为准。
依赖数量(cargo_deps):统计根目录 Cargo.lock 中 [[package]] 段的行数,作为依赖膨胀的长期趋势指标,并带 count > 10 的健全性断言(main.rs#L321-L334)。
5. LSP 基准:真实请求回放
LSP 基准由 tests/bench/lsp.rs 实现,思路是“录制回放”:用 include_bytes! 把录制的 JSON-RPC 请求序列(如 testdata/db_messages.json、testdata/deco_apps_requests.json)编译进二进制,运行时通过 tower-lsp 的 LspClientBuilder 启动 deno lsp,回放悬停、补全、高亮、代码编辑等请求并计时,例如 bench_deco_apps_edits 会回放一个完整应用仓库的编辑请求流。结果以 lsp_exec_time 计入总输出。该模块同时可经由独立目标 lsp_bench_standalone(tests/bench/lsp_bench_standalone.rs)单独执行。
6. 同目录下的 JS 侧基准脚本
tests/bench/ 目录下还有一批使用 Deno.bench API 编写的 JavaScript 基准,用于测量 JS 侧热点,例如:
- tests/bench/deno_common.js:
Date.now()、performance.now()(注释标注为“非常轻量的 op,理应高度可优化”)、new URL(...)解析、atob/btoaBase64 往返、open_file_sync、从/dev/zero读字节等,多数用例显式指定迭代次数(如{ n: 5e5 }); - tests/bench/command.js:以
Deno.Command启动外部进程(echo、cat 128kb)的开销基准; - 其余
streams.js、tcp.js、sqlite.js、webstorage.js、url_parse.js等文件则覆盖对应运行时 API。
这些脚本与 main.rs 的进程级基准互补:前者测量 Deno API 的函数级吞吐,后者测量整条 CLI 链路的端到端表现。
7. 结果产出:target/bench.json 与数据管道
所有指标最终汇聚为 main.rs#L337-L357 中的 BenchResult 结构并序列化为 JSON,字段与来源的对应关系如下:
| 字段 | 来源 |
|---|---|
created_at / sha1 |
当前 UTC 时间戳与 git rev-parse HEAD,用于按提交对齐历史数据 |
benchmark |
exec_time 的六项统计量(源码 TODO 注释说明其历史名称应为 exec_time) |
binary_size |
第 4 节的四类体积 |
cargo_deps |
依赖包数量 |
lsp_exec_time |
LSP 请求回放耗时 |
max_memory |
time -v 解析的峰值内存 |
syscall_count / thread_count |
strace 统计 |
该 JSON 被写入 target/bench.json(main.rs#L483-L486)。后续的聚合由 tools/build_benchmark_jsons.js 完成:它以构建目录下的 bench.json 为输入,为基准数据仓库生成按基准维度拆分的 JSON 序列。README 中“benchmark plots”一节即指向这一数据管道的下游——基准趋势图由外部托管的仪表盘渲染(README 给出的两个地址为外部站点,本文按规范不输出该链接)。源码中的 TODO 注释也表明,历史上数据曾提交至独立的 benchmark_data 仓库,该流程已标记为弃用。
8. 实践要点与前提条件
结合源码,实际跑这套基准时值得注意:
- 指定被测二进制:
main()优先读取环境变量DENO_BENCH_EXE,缺省使用test_util::deno_exe_path()(即本树构建出的 deno 可执行文件,main.rs#L388-L393),因此测量的是当前工作区代码而非安装的发布版; - 执行目录:程序会
set_current_dir到仓库根,用例中出现的tests/testdata/...相对路径依赖这一点; - 过滤语义:
--后参数等于--bench被视为模式开关而非过滤器;传入未知名称会使基准列表清空,但仍会走完整流程写出空结果,建议过滤前先对照第 2 节的清单; - 平台差异:
strace组仅在 Linux 生效,mem_usage依赖 GNUtime;hyperfine通过test_util的预构建工具路径获取,需构建流程已准备好该工具; - 结果解读:exec_time 采用
mean/stddev/user/system/min/max六元组,跨提交对比时建议以mean为主、stddev为噪声参考;sha1字段可用于把一次运行精确对齐到源码提交。
9. 小结
tests/bench/README.md 虽然简短,但点出了 Deno 基准体系的两条主线:cargo bench --bench deno_bench -- <组名> 的过滤式运行,以及结果向外部基准图谱的持续上报。深入源码后可以确认,这套体系由 tests/bench/Cargo.toml 声明的 deno_bench/lsp_bench_standalone 目标驱动,tests/bench/main.rs 以 hyperfine、strace、time -v 等系统工具组合出执行时间、内存、系统调用、二进制体积、依赖数五类进程级指标,tests/bench/lsp.rs 提供 LSP 请求回放,同目录的 Deno.bench 脚本补充 JS 侧函数级基准;最终所有指标连同提交号写入 target/bench.json,经 tools/build_benchmark_jsons.js 拆分为时序数据供趋势分析使用。理解这条从“过滤命令”到“趋势图”的完整链路,是复现和解读 Deno 官方性能数据的起点。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0627
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00