Rust软件性能评测项目
Rust Bench — Rust 性能基准测试与分析框架
一个用于 Rust 程序运行时性能基准测试、数据采集、终端展示和性能分析的完整工具链。基于 rustc-perf 构建,扩展了增强的运行时基准测试能力、自定义指标支持和 TUI 比较界面。
核心特性
- ✅ 运行时基准测试(Runtime Benchmark) — 完整的 Rust 标准库基准测试套件和自定义基准测试,基于硬件性能计数器(
perf_event) - ✅ 终端数据展示 — 基准测试过程中实时输出 min/mean/stddev 统计数据,以及交互式 TUI(
bench_cmp)用于比较两个版本之间的结果 - ✅ 性能分析(Profile) — 支持 Cachegrind(指令级)和 perf-record(采样式)的运行时性能分析
目录
快速开始
前置条件
- Rust stable 工具链
- Linux 系统(需要
perf_event硬件计数器支持) - 设置 perf event 权限:
sudo bash -c 'echo -1 > /proc/sys/kernel/perf_event_paranoid'
构建
cargo build --release
运行运行时基准测试
# 运行所有运行时基准测试
./target/release/collector bench_runtime_local <RUSTC>
# 只运行指定的 benchmark 组
./target/release/collector bench_runtime_local <RUSTC> --group std
# 按名称前缀过滤 benchmark
./target/release/collector bench_runtime_local <RUSTC> --include find_existing
./target/release/collector bench_runtime_local <RUSTC> --exclude hashmap
# 复用已编译的产物,加速重复运行
./target/release/collector bench_runtime_local <RUSTC> --no-isolate
# 指定数据库和迭代次数
./target/release/collector bench_runtime_local <RUSTC> --db my_results.db --iterations 10
其中 <RUSTC> 是 rustc 可执行文件的路径(例如 path/to/stage1/bin/rustc),或者以 + 开头的工具链名(例如 +nightly)。
常用选项(bench_runtime_local)
| 选项 | 说明 |
|---|---|
--id <ID> |
基准测试结果的标识符 |
--db <DATABASE> |
数据库文件路径(默认:results.db) |
--iterations <N> |
每个 benchmark 的迭代次数(默认:5) |
--include <PREFIX> |
只包含匹配此前缀的 benchmark |
--exclude <PREFIX> |
排除匹配此前缀的 benchmark |
--group <GROUP> |
只编译和运行指定的 benchmark 组 |
--no-isolate |
复用之前编译的 benchmark 产物 |
--purge |
在测试前清除该 artifact 的旧数据 |
比较结果
# 交互式 TUI 比较(交互选择 artifact)
./target/release/collector bench_cmp --db results.db
运行性能分析
# Cachegrind 分析(分析组内所有 benchmark)
./target/release/collector profile_runtime <RUSTC> cachegrind
# 分析指定的组
./target/release/collector profile_runtime <RUSTC> cachegrind --group std
# perf-record 分析
./target/release/collector profile_runtime <RUSTC> perf-record
# 比较两个 rustc 版本(cachegrind diff)
./target/release/collector profile_runtime <RUSTC> cachegrind --rustc2 <RUSTC2>
# 精确 Cachegrind 模式(需要 Valgrind ≥ 3.22)
DEP_VALGRIND=<valgrind-install>/include cargo run --release --bin collector \
--features precise-cachegrind profile_runtime <RUSTC> cachegrind
功能介绍
3.1 运行时基准测试
运行时基准测试套件测量用特定版本的 rustc 编译后的 Rust 程序执行速度。基准测试以 组(group) 为单位组织——每个组是 collector/runtime-benchmarks/ 下的一个独立 crate。
标准库基准测试组
| 组名 | 测试内容 |
|---|---|
std |
HashMap、HashSet、IO(BufRead/Copy/Cursor/Impls)、Path、Time |
core |
核心库基准测试 |
alloc |
内存分配器基准测试 |
自定义基准测试组
| 组名 | 测试内容 |
|---|---|
fmt |
格式化基准测试 |
hashmap |
哈希表性能 |
compression |
压缩算法 |
css |
CSS 解析 |
nbody |
N 体模拟 |
nes |
NES 模拟器 |
parsing |
文本解析 |
raytracer |
光线追踪 |
svg |
SVG 处理 |
text-search |
文本搜索 |
bufreader |
缓冲读取 |
daft-vllm |
自定义基准测试示例(包含自定义指标、JSON 报告、额外命令行参数) |
采集指标
基准测试框架使用 Linux perf_event_open 系统调用采集硬件性能计数器:
| 指标 | 说明 |
|---|---|
instructions:u |
CPU 指令数 |
cycles:u |
CPU 周期数 |
wall-time |
墙钟时间 |
branch-misses |
分支预测失败 |
cache-misses |
缓存未命中 |
cache-references |
缓存引用 |
max-rss |
最大常驻内存 |
| 自定义指标 | 任意用户定义的指标 |
每个 benchmark 分两轮执行:一轮采集硬件性能计数器,一轮采集墙钟时间,确保结果准确且互不干扰。
3.2 终端数据展示
实时 Benchmark 输出
每个 benchmark 完成后,统计信息会打印到终端:
Finished std/find_existing (1/42)
[Instructions]: min: 1,234,567 mean: 1,234,890 stddev: 234
[Cycles]: min: 456,789 mean: 457,012 stddev: 156
[Wall time [ns]]: min: 234,000 mean: 235,000 stddev: 2,000
[Branch misses]: min: 1,234 mean: 1,245 stddev: 12
[Cache misses]: min: 56 mean: 58 stddev: 3
[Memory [kb]]: min: 2,048 mean: 2,048 stddev: 0
- 自定义数值指标(如
throughput_tok/s、latency_p99_ms)也会自动显示 - JSON 报告以
<JSON data> (view in bench_cmp)标记显示 - 进度追踪:
Finished <group>/<benchmark> (<current>/<total>)
bench_cmp — 交互式 TUI 比较界面
bench_cmp 命令提供基于终端的交互式 UI(基于 ratatui),用于比较两个 artifact 版本之间的基准测试结果:
./target/release/collector bench_cmp --db results.db
功能特性:
- 模式切换 — 按
M在编译(Compile)和运行时(Runtime)比较模式间切换 - 摘要面板 — 顶部显示回归/改进摘要统计
- 详细表格 — Benchmark 名称、前值(Before)、后值(After)、变化幅度(含 95% 置信区间)
- 指标切换 — 按
A/S在可用指标间循环(instructions、cycles、wall-time 等) - 显著性过滤 — 按
F切换显示所有结果或仅显示显著变化 - 目标平台切换 — 按
1/2切换基准/修改版本的目标平台 - JSON 详情视图 — 在 JSON 行(如
perf-record数据)上按Enter打开可滚动的详情视图 - 导航 —
↑/↓导航,q/Esc退出
3.3 性能分析(Profile)
profile_runtime 子命令使用以下两种 profiler 之一对运行时基准测试进行性能分析:
Cachegrind
- 用途:指令级分析,结果近乎确定性
- 输出:原始数据(
cgout前缀)+ 人类可读的注解输出(cgann前缀) - Diff 支持:使用
--rustc2时,生成两个版本的差异对比文件 - 精确模式:使用
--features precise-cachegrind时,仅对实际 benchmark 代码进行计数(需要 Valgrind ≥ 3.22)
perf-record
- 用途:采样式分析,适合发现热点函数
- 输出:原始数据(
perf前缀)+ 注解报告(perfreport前缀) - 开销:可忽略不计
Profile 选项
| 选项 | 说明 |
|---|---|
<PROFILER> |
cachegrind 或 perf-record |
<RUSTC> |
rustc 可执行文件路径或 +toolchain 名 |
--group <GROUP> |
仅分析指定的 benchmark 组 |
--iterations <N> |
分析迭代次数(默认:5) |
--include <PREFIX> |
按前缀包含 benchmark |
--exclude <PREFIX> |
按前缀排除 benchmark |
--rustc2 <RUSTC> |
第二个 rustc,用于对比分析(cachegrind 支持 diff) |
Benchmark 编写指南
4.1 使用 #[bench] 宏
推荐的 Rust 函数性能测试方式。#[bench] 宏会自动将您的函数注册到全局 benchmark 组。
第一步:创建或选择 benchmark 组
每个 benchmark 组是 collector/runtime-benchmarks/ 下的一个二进制 crate。按照约定,如果组目录名为 foo,则 crate 名应为 foo-bench。
第二步:添加 benchlib 依赖
[dependencies]
benchlib = { path = "../../benchlib" }
第三步:使用 #[bench] 编写 benchmark
use std::collections::HashMap;
use benchlib::benchmark::{bench, Bencher};
#[bench]
fn find_existing(b: &mut Bencher) {
let mut m = HashMap::new();
for i in 1..1001 {
m.insert(i, i);
}
b.iter(
|| {}, // constructor:准备每次迭代的数据(不被测量)
|_| { // bench:实际被测量的代码
for i in 1..1001 {
m.contains_key(&i);
}
},
);
}
#[bench]
fn grow_by_insertion(b: &mut Bencher) {
let mut m = HashMap::new();
for i in 1..1001 {
m.insert(i, i);
}
let mut k = 1001;
b.iter(
|| {},
|_| {
m.insert(k, k);
k += 1;
},
);
}
第四步:设置 main.rs
fn main() {
benchlib::benchmark::run_global_benchmark_group();
}
Bencher::iter(constructor, bench) 工作原理
constructor:每次测量前调用,用于准备输入数据。其返回值会传递给bench。此代码 不被 测量。bench:接收constructor的输出。此代码 会被 硬件性能计数器和墙钟时间所测量。- 系统自动执行 3 次热身迭代,随后进行 N 次正式测量(可通过
--iterations配置)。
4.2 自定义 Benchmark(daft-vllm 示例)
当需要完全控制测量逻辑、自定义指标、JSON 报告或自定义命令行参数时,使用 benchlib::custom API。
核心 API
| API | 用途 |
|---|---|
benchlib::custom::init(&[names]) |
初始化 CLI,解析参数,返回 BenchAction |
benchlib::custom::parse_extra_args(&extra) |
解析 -- 后传入的自定义参数 |
BenchmarkSample::new(duration) |
创建包含墙钟时间的样本 |
sample.set("metric", value) |
设置自定义数值指标 |
BenchmarkResult::with_samples(name, samples) |
从样本创建结果 |
result.add_report("name", &data) |
添加 JSON 报告(任意 Serialize 类型) |
result.add_report(METRIC_PERF_RECORD, &entries) |
添加 perf-record 分析数据 |
output_message(&mut stdout, msg) |
以 JSON 格式将结果输出到 stdout |
完整示例(来自 daft-vllm/src/main.rs)
use benchlib::clap;
use benchlib::comm::messages::{
BenchmarkMessage, BenchmarkResult, BenchmarkSample,
PerfRecordEntry, METRIC_PERF_RECORD,
};
use benchlib::comm::output_message;
use benchlib::custom::{init, parse_extra_args, BenchAction};
use std::time::Duration;
/// 自定义参数,从命令行 `-- ...` 后解析。
#[derive(clap::Parser, Debug)]
struct VllmArgs {
#[arg(long, default_value = "default-model")]
model: String,
#[arg(long, default_value_t = 16)]
batch_size: u32,
}
fn main() -> benchlib::anyhow::Result<()> {
let action = init(&["daft-vllm-example"])?;
match action {
BenchAction::Run { iterations, benchmarks, extra } => {
let vllm_args: VllmArgs = parse_extra_args(&extra)?;
let mut stdout = std::io::stdout().lock();
for name in &benchmarks {
let mut samples = Vec::with_capacity(iterations as usize);
for _ in 0..iterations {
// 在这里编写你的 benchmark 逻辑
let mut sample = BenchmarkSample::new(Duration::from_millis(0));
// 添加自定义数值指标
sample.set("throughput_tok/s", 1234.5);
sample.set("latency_p99_ms", 42.0);
samples.push(sample);
}
let mut result = BenchmarkResult::with_samples(name.clone(), samples);
// 添加结构化 JSON 报告
#[derive(serde::Serialize)]
struct LatencyDist { p50: f64, p90: f64, p99: f64 }
result.add_report("latency_distribution", &LatencyDist {
p50: 12.5, p90: 35.2, p99: 42.0,
});
// 添加 perf-record 分析数据
let perf_entries = vec![
PerfRecordEntry {
function: "engine::execute_plan".to_string(),
delay: 15.3,
time_rate: 0.35,
sample_count: 350,
is_rust: true,
category: "compute".to_string(),
shared_object: "libdaft.so".to_string(),
},
];
result.add_report(METRIC_PERF_RECORD, &perf_entries);
output_message(&mut stdout, BenchmarkMessage::Result(result))?;
}
}
BenchAction::Profile(profile_args) => {
let vllm_args: VllmArgs = parse_extra_args(&profile_args.extra)?;
for _ in 0..profile_args.iterations {
// 在这里编写你的 profiling 逻辑
}
}
}
Ok(())
}
运行自定义 Benchmark
# 使用默认参数运行
./target/release/collector bench_runtime_local <RUSTC> --group daft-vllm
# 通过 -- 传递自定义参数
./target/release/collector bench_runtime_local <RUSTC> --group daft-vllm \
-- --model gpt2 --batch-size 32
项目结构
rust-bench/
├── collector/ # 数据收集器
│ ├── benchlib/ # 核心基准测试库
│ │ ├── src/benchmark.rs # #[bench] 宏 + BenchmarkGroup + Bencher
│ │ ├── src/custom.rs # 自定义 benchmark 框架(init/BenchAction)
│ │ ├── src/comm/messages.rs # 消息协议 + 指标定义
│ │ └── src/measure/ # 性能计数器测量(perf_event)
│ ├── benchlib-macros/ # #[bench] 过程宏实现
│ ├── runtime-benchmarks/ # 运行时基准测试套件
│ │ ├── std/ # 标准库测试(HashMap、IO、Path、Time)
│ │ ├── core/ # 核心库测试
│ │ ├── alloc/ # 分配器测试
│ │ ├── fmt/ # 格式化测试
│ │ ├── hashmap/ # 哈希表测试
│ │ ├── daft-vllm/ # 自定义 benchmark 示例
│ │ └── ... # 更多 benchmark 组
│ ├── compile-benchmarks/ # 编译时基准测试套件
│ └── src/
│ ├── bin/collector.rs # CLI 入口
│ ├── runtime/mod.rs # 运行时 benchmark 执行引擎
│ ├── runtime/profile.rs # 运行时 profiling(cachegrind、perf-record)
│ └── compare/screen.rs # TUI 比较界面(bench_cmp)
├── database/ # 数据库层(SQLite/Postgres)
├── site/ # Web 前端(可选)
├── Cargo.toml # Workspace 配置
└── README.md
许可证
原始 rustc-perf 代码基于 MIT 许可证,由 Reuse Specification 管理。
编译时基准测试有各自独立的许可证,详见 collector/compile-benchmarks/REUSE.toml 文件。
旋武团队(Xuanwu Team) 所做的修改和扩展同样采用 MIT 许可证。
Copyright (c) Xuanwu Team. All rights reserved.