为测试目的对函数调用进行计时的最简单方法是什么



所以我在Rust中还是有点绿色的,但来自Python的我发现这个场景通常非常令人困惑。

我喜欢Python,因为如果你想对代码块或函数调用计时,它非常容易:

print(timeit('a = "hee hee la le dah"; my_awesome_fn()', number = 1_000, globals=globals()))

然后只调用CCD_ 1或者更好;运行";按钮,您就可以调用该脚本。但我很难在Rust中找到功能等价物。

我知道Rust生态系统中有一个概念叫做基准测试,一些像criterion这样的库就是为了这个目的而存在的。问题是,我对高等数学和统计学一无所知(本质上可以把我当成一个愚蠢的白痴(,我怀疑我能从这样的框架或工具中受益匪浅。

因此,我只是好奇如何在cargo中使用tests来测试Rust中的代码块,甚至更好的是测试函数调用。

例如,假设我在rust中有类似的函数,我想多次调用它,然后检查性能如何变化等:

pub fn my_awesome_fn() {
trace!("getting ready to do something cool...");
std::thread::sleep(std::time::Duration::from_millis(500));
info!("finished!");
}

我怎么能简单地把这个函数my_awesome_fn计时在rust中呢?我想我正在寻找类似于python中的timeit或类似的东西。理想情况下,它应该是向前使用的,并假设我对自己正在做的事情一无所知。我很好奇是否有一个现有的库或框架可以用于此目的。

免责声明:我从未使用过timeit

一个非常快速的解决方案是编写一个函数,如:

fn timeit<F: Fn() -> T, T>(f: F) -> T {
let start = SystemTime::now();
let result = f();
let end = SystemTime::now();
let duration = end.duration_since(start).unwrap();
println!("it took {} seconds", duration.as_secs());
result
}

你可以用它来";"包裹";另一个函数调用:

fn main() {
let x = timeit(|| my_expensive_function());
}

但是,如果您试图了解函数为性能优化所花费的时间,则这种方法可能过于粗糙。

问题是我对高级数学和统计学一无所知

这可以说是criterion的主要优势之一;将数学抽象掉";,从某种意义上说。

它使用统计方法让你更好地了解基准测试运行之间的差异是否是";"随机性";,或者每次运行的代码之间是否存在有意义的差异。

对于最终用户来说,它本质上是给你一个报告,说";观察到显著的变化";或";没有观察到显著的变化";。它所做的远不止于此,但为了充分掌握其能力,它可能值得一读;假设检验";。

如果你可以使用夜间Rust,你也可以使用#[bench]测试:

#![feature(test)]
extern crate test;
#[bench]
fn bench_my_func(b: &mut Bencher) {
b.iter(|| my_func(black_box(100));
}

可以使用cargo bench运行。这些比python script.py0更容易设置,但做的有趣的统计数据较少(即你必须自己做(,但它们是一个非常";快速而肮脏";了解代码运行时的感觉。

警告一下,基准测试代码很难。你可能会对幕后发生的事情感到惊讶,你可能会发现自己的基准测试是错误的。

普通的";gotchas";是:

  • CCD_;无用的";black_box函数可以用来向优化器隐藏某些数据的含义,尽管它本身也有开销
  • 类似地,LLVM会对多项式进行一些略显诡异的优化。您可能会发现,您的函数调用正在被优化为常量/简单算术。在某些情况下,这太棒了!您编写函数的方式使得LLVM可以将其简化为一些琐碎的事情。在其他情况下,您现在只是在CPU上对乘法指令进行基准测试,这不太可能是您想要的。运用你的最佳判断
  • 基准测试错误的东西——有些东西比其他东西贵得多,对于有python背景的人来说,这些方式可能很奇怪。例如,克隆一个String(即使是很短的一个(可能比找到第一个字符慢2-3个数量级。考虑以下内容:
fn str_len(s: String) -> usize {
s.len()
}
#[bench]
fn bench_str_len(b: &mut Bencher) {
let s = String::from("hello");  
b.iter(|| str_len(s.clone()));
}

因为String::clone涉及堆分配,而s.len()只是一个字段访问,所以它将主导结果。相反,如果str_len&str,它将变得更具代表性(尽管这是一个人为的情况(。

TLDR小心您的基准测试代码在做什么。铁锈游乐场的";视图组件";tool(或godbolt.org(是你的朋友。你不需要成为装配专家,但它可以帮助你了解的幕后情况

来源:https://rust-lang-nursery.github.io/rust-cookbook/datetime/duration.html

use std::time::{Duration, Instant};
fn main() {
let start = Instant::now();
expensive_function();
let duration = start.elapsed();
println!("Time elapsed in expensive_function() is: {:?}", duration);
}

最新更新