首页 > 解决方案 > 如何在 Rust 中为 Criterion 基准创建随机输入

问题描述

我正在尝试annotate使用 Criterion 基准测试库对例程进行基准测试。该例程正在检查一个&[&str]参数(2D 方字符串)并返回Vec<String>,我怀疑它的执行时间可能取决于参数的内容。因此,我想随机化单个工作负载的输入,在这种情况下,工作负载意味着字符串参数的宽度和高度(通常是宽度 == 高度)。

我注意到 Criterion 具有采用两个闭包而不是单个定时闭包、一个 Setup 和一个 Routine(定时)的方法iter_batchediter_batched_ref它们都是 FnMut 所以所有捕获的变量都是可变引用(据我所知)。因此,我尝试Vec<Vec<u8>>在常规仅调用我的annotate函数时随机化设置中 a 的内容。

fn plant_mines<R: Rng + ?Sized>(mfield: &mut Vec<Vec<u8>>, rng: &mut R) {
    mfield.into_iter().flatten().for_each(|x| *x = if rng.gen::<f32>() < MINE_RATE {MINE} else {EMPTY});
}

pub fn benchmark(c: &mut Criterion) {
    let mut rng = rand_pcg::Pcg64Mcg::seed_from_u64(RAND_SEED);
    let bench_params : [(usize, usize); 4] = [
        (3, 3),
        (5, 5),
        (8, 8),
        (16, 16)
    ];

    let dims2str = |d : (usize, usize)| { let (w, h) = d; format!("{:}, {:}", w, h) };

    let mut group = c.benchmark_group("Minefield Benchmark");
    for dims in bench_params.iter() {
        group.bench_with_input(BenchmarkId::from_parameter(dims2str(*dims)), dims, |b, (w, h)| {
            let mut mf : Vec<Vec<u8>> = (0..*h).map(|_| iter::repeat(EMPTY).take(*w).collect::<Vec<_>>()).collect();
            let mf_ref = mf.iter().map(|vu8| str::from_utf8(&vu8).unwrap()).collect::<Vec<&str>>();

            b.iter_batched(|| {
                plant_mines(&mut mf, &mut rng);
            }, |_| annotate(&mf_ref), BatchSize::SmallInput);
        });
    }
    group.finish();
}

通常,设置函数应该为要使用的例程生成输入,但在这种情况下,我省略了它们。mf_ref持有一个参考向量,Vec<Vec<u8>>我不想在定时例程中创建/分配这个向量,否则,它将为基准测试产生更多噪音。不幸的是,借阅检查员并不高兴。

在此处输入图像描述

我试图在 Setup 闭包中创建输入向量,但它无法将参考向量返回到定时例程。我试图了解 或 中的任何一个是否Cell可以RefCell解决问题,但我无法掌握足够的知识来在这里申请(如果可能的话)。这Cell是不可能的,因为Vec没有实现Copy我理解的特征。

如何在仅对annotate函数进行基准测试并为每批样本使用随机输入的同时满足编译器的要求?

标签: rustbenchmarkingrust-criterion

解决方案


您没有按预期使用批处理机制,这样做将解决您的冲突借用问题。mf_ref您应该在 setup 闭包中创建mf并返回它,而不是关闭基准例程。这样,就不会共享对mf.

b.iter_batched(|| {
    let mut mf : Vec<Vec<u8>> = (0..*h).map(|_| iter::repeat(EMPTY).take(*w).collect::<Vec<_>>()).collect();
    plant_mines(&mut mf, &mut rng);
    mf
}, |mf| annotate(&mf), BatchSize::SmallInput);

请注意,这也摆脱了mf_ref' 转换为&str. &'static str除非字符串全部都是(如果它们始终是对应于 MINE 或 EMPTY 的字符串,那么您可以只有两个对应的字符串文字,这可能会在此处实现(容易) );您需要重新设计您的annotate,以便它适用于拥有的结构或仅引用&'static strs 的结构。(或者你可以把它放在 旁边的基准闭包中annotate,但是你正在测量它。)

当然,您不应该仅仅为了基准而重塑您的代码,但这可能会给您在其他地方的灵活性,因为长期引用通常不方便。我不能确切地告诉你该怎么做,因为我不知道and的annotate作用或价值是什么。MINEEMPTY


推荐阅读