Rust性能基准测试库criterion2的使用,criterion2提供精确的微基准测试和统计分析功能

Rust性能基准测试库criterion2的使用

特性

  • 统计分析:能够检测性能变化,并量化变化的程度
  • 图表生成:使用gnuplot生成详细的基准测试结果图表
  • 稳定版兼容:无需安装nightly Rust即可进行基准测试

快速开始

首先需要安装gnuplot以生成图表。

在项目的Cargo.toml中添加以下内容:

[dev-dependencies]
criterion = { version = "0.4", features = ["html_reports"] }

[[bench]]
name = "my_benchmark"
harness = false

然后,在benches/my_benchmark.rs中创建基准测试文件:

use criterion::{black_box, criterion_group, criterion_main, Criterion};

// 定义一个斐波那契函数用于测试
fn fibonacci(n: u64) -> u64 {
    match n {
        0 => 1,
        1 => 1,
        n => fibonacci(n-1) + fibonacci(n-2),
    }
}

// 基准测试函数
fn criterion_benchmark(c: &mut Criterion) {
    c.bench_function("fib 20", |b| b.iter(|| fibonacci(black_box(20))));
}

// 定义基准测试组
criterion_group!(benches, criterion_benchmark);
// 定义基准测试主函数
criterion_main!(benches);

运行cargo bench命令后,你将看到类似以下的输出:

     Running target/release/deps/example-423eedc43b2b3a93
fib 20                  time:   [26.029 us 26.251 us 26.505 us]
Found 11 outliers among 99 measurements (11.11%)
  6 (6.06%) high mild
  5 (5.05%) high severe

完整示例

下面是一个更完整的基准测试示例,展示了如何比较不同实现的性能:

use criterion::{black_box, criterion_group, criterion_main, Criterion};

// 递归实现的斐波那契
fn fibonacci_recursive(n: u64) -> u64 {
    match n {
        0 => 1,
        1 => 1,
        n => fibonacci_recursive(n-1) + fibonacci_recursive(n-2),
    }
}

// 迭代实现的斐波那契
fn fibonacci_iterative(n: u64) -> u64 {
    if n == 0 || n == 1 {
        return 1;
    }
    
    let mut a = 1;
    let mut b = 1;
    
    for _ in 2..=n {
        let c = a + b;
        a = b;
        b = c;
    }
    
    b
}

// 基准测试函数
fn criterion_benchmark(c: &mut Criterion) {
    let mut group = c.benchmark_group("Fibonacci");
    
    // 测试递归实现
    group.bench_function("recursive 20", |b| {
        b.iter(|| fibonacci_recursive(black_box(20)))
    });
    
    // 测试迭代实现 
    group.bench_function("iterative 20", |b| {
        b.iter(|| fibonacci_iterative(black_box(20)))
    });
    
    group.finish();
}

// 定义基准测试组
criterion_group!(benches, criterion_benchmark);
// 定义基准测试主函数
criterion_main!(benches);

这个示例会:

  1. 比较递归和迭代两种斐波那契实现
  2. 使用benchmark_group来组织相关测试
  3. 对每种实现运行多次测试以获得准确结果

运行后你会看到两者的性能对比,帮助你选择更优的实现方案。

Criterion.rs是优化Rust代码性能的强大工具,它通过统计学方法确保测试结果的可靠性,让你能够精确测量代码优化带来的性能提升。


1 回复

Rust性能基准测试库criterion2使用指南

简介

criterion2是Rust生态中一个强大的性能基准测试库,它提供了精确的微基准测试功能和统计分析能力。相比标准库中的基准测试功能,criterion2提供了更详细的统计数据和可视化报告,能够帮助开发者更准确地测量和优化代码性能。

主要特性

  • 精确的微秒级基准测试
  • 高级统计分析功能
  • 自动检测性能回归
  • 生成HTML报告
  • 支持对比基准测试
  • 无需nightly Rust即可使用

使用方法

1. 添加依赖

首先在Cargo.toml中添加criterion2依赖:

[dev-dependencies]
criterion = "0.4"

2. 创建基准测试

在项目的benches目录下创建基准测试文件(如benches/my_benchmark.rs):

use criterion::{black_box, criterion_group, criterion_main, Criterion};

fn fibonacci(n: u64) -> u64 {
    match n {
        0 => 1,
        1 => 1,
        n => fibonacci(n-1) + fibonacci(n-2),
    }
}

fn bench_fib(c: &mut Criterion) {
    c.bench_function("fib 20", |b| b.iter(|| fibonacci(black_box(20))));
}

criterion_group!(benches, bench_fib);
criterion_main!(benches);

3. 运行基准测试

执行以下命令运行基准测试:

cargo bench

高级用法

对比基准测试

可以比较不同实现的性能:

use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion};

fn fibonacci_recursive(n: u64) -> u64 {
    match n {
        0 => 1,
        1 => 1,
        n => fibonacci_recursive(n-1) + fibonacci_recursive(n-2),
    }
}

fn fibonacci_iterative(n: u64) -> u64 {
    let mut a = 1;
    let mut b = 1;
    
    for _ in 0..n {
        let c = a + b;
        a = b;
        b = c;
    }
    
    a
}

fn bench_fibs(c: &mut Criterion) {
    let mut group = c.benchmark_group("Fibonacci");
    
    for i in [20, 21, 22].iter() {
        group.bench_with_input(BenchmarkId::new("Recursive", i), i, 
            |b, i| b.iter(|| fibonacci_recursive(black_box(*i))));
            
        group.bench_with_input(BenchmarkId::new("Iterative", i), i, 
            |b, i| b.iter(|| fibonacci_iterative(black_box(*i))));
    }
    
    group.finish();
}

criterion_group!(benches, bench_fibs);
criterion_main!(benches);

参数化基准测试

use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion};

fn process_data(data: &[u32]) -> u32 {
    data.iter().sum()
}

fn bench_processing(c: &mut Criterion) {
    let sizes = [100, 1000, 10000];
    
    let mut group = c.benchmark_group("Processing");
    for size in sizes.iter() {
        let data: Vec乌32> = (0..*size).collect();
        group.bench_with_input(BenchmarkId::from_parameter(size), &data, 
            |b, data| b.iter(|| process_data(black_box(data))));
    }
    group.finish();
}

criterion_group!(benches, bench_processing);
criterion_main!(benches);

自定义配置

可以自定义基准测试的配置:

use criterion::{criterion_group, criterion_main, Criterion, SamplingMode};

fn bench_config(c: &mut Criterion) {
    let mut group = c.benchmark_group("sample-size-example");
    group.sample_size(50) // 减少样本数量以加快测试
         .sampling_mode(SamplingMode::Flat); // 线性采样
    
    group.bench_function("fast-function", |b| b.iter(|| 1 + 1));
    group.finish();
}

criterion_group!{
    name = benches;
    config = Criterion::default().sample_size(10);
    targets = bench_config
}
criterion_main!(benches);

查看报告

运行cargo bench后,criterion2会生成详细的HTML报告,位于target/criterion/reports目录下。报告包含:

  • 执行时间分布
  • 变化趋势
  • 统计摘要
  • 性能对比图表

最佳实践

  1. 使用black_box防止编译器过度优化
  2. 为基准测试函数选择有意义的名称
  3. 测试不同规模的输入以了解算法复杂度
  4. 关注统计显著性而不仅仅是平均时间
  5. 在稳定的环境中运行基准测试(避免后台任务干扰)

criterion2是优化Rust代码性能的强大工具,通过其详细的统计数据和可视化报告,开发者可以更科学地评估和改进代码性能。

完整示例demo

下面是一个完整的criterion2基准测试示例,包含参数化测试和对比测试:

// benches/complete_demo.rs
use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion};

// 三种不同的字符串拼接实现
fn concat_plus(mut s: String, parts: &[&str]) -> String {
    for part in parts {
        s += part;
    }
    s
}

fn concat_push_str(mut s: String, parts: &[&str]) -> String {
    for part in parts {
        s.push_str(part);
    }
    s
}

fn concat_join(s: String, parts: &[&str]) -> String {
    s + &parts.join("")
}

// 参数化基准测试函数
fn bench_string_concat(c: &mut Criterion) {
    let parts_sizes = [10, 100, 1000];
    let test_string = "test".repeat(10);
    
    let mut group = c.benchmark_group("String Concatenation");
    
    for size in parts_sizes.iter() {
        let parts: Vec<&str> = (0..*size).map(|_| &test_string[..]).collect();
        
        group.bench_with_input(
            BenchmarkId::new("Using +=", size), 
            &parts,
            |b, parts| b.iter(|| {
                concat_plus(black_box(String::new()), black_box(parts))
            })
        );
        
        group.bench_with_input(
            BenchmarkId::new("Using push_str", size), 
            &parts,
            |b, parts| b.iter(|| {
                concat_push_str(black_box(String::new()), black_box(parts))
            })
        );
        
        group.bench_with_input(
            BenchmarkId::new("Using join", size), 
            &parts,
            |b, parts| b.iter(|| {
                concat_join(black_box(String::new()), black_box(parts))
            })
        );
    }
    
    group.finish();
}

criterion_group!{
    name = benches;
    config = Criterion::default().sample_size(20);
    targets = bench_string_concat
}
criterion_main!(benches);

这个完整示例演示了:

  1. 三种不同的字符串拼接实现
  2. 对不同大小的输入进行参数化测试
  3. 使用black_box防止编译器优化
  4. 自定义样本数量为20
  5. 清晰的benchmark分组和命名

运行这个基准测试后,criterion2会生成详细的对比报告,帮助开发者分析不同实现和不同输入规模下的性能差异。

回到顶部