cargo install flamegraph然后,你可以使用“cargo flamgraph”命令来测试编译好的二进制文件。
每个框代表一个堆栈帧或一个函数调用。高度表示堆栈深度,最近的堆栈帧位于顶部,较旧的堆栈帧位于底部。子框架驻留在调用它们的函数之上。帧的宽度表示一个函数或它的子函数被处理的总时间。您可以将鼠标悬停在一个框架上以获取更多细节,并单击一个框架展开它以获得更细粒度的视图。每个帧的颜色并不重要,并且是随机的,除非你使用 --deterministic参数,这将在运行期间保持功能/颜色的一致性。
[dev-dependencies] criterion = { version = "0.5.3", features = ["html_reports"] } [[bench]] name = "my_benchmark"然后,你可以在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在进行优化时,请始终记住对代码进行基准测试,以确保所做的更改确实提高了程序的性能。如果基准测试没有显示出足够显著的速度改进,那么可能就不值得进行优化。
一定要仔细考虑哪种数据结构最适合你的问题,因为选择正确的数据结构会对代码的性能产生巨大影响。
use std::collections::HashMap; fn get_data_from_database(id: u32, cache: &mut HashMap<u32, String>) -> String { if let Some(data) = cache.get(&id) { return data.clone(); } let data = perform_expensive_database_query(id); cache.insert(id, data.clone()); data } fn perform_expensive_database_query(id: u32) -> String { // 模拟一个昂贵的数据库查询 println!("Performing database query for ID {}", id); // ... 数据库访问和数据检索 ... let data = format!("Data for ID {}", id); data } fn main() { let mut cache: HashMap<u32, String> = HashMap::new(); // 多次从数据库查询数据 for _ in 0..5 { let id = 42; let data = get_data_from_database(id, &mut cache); println!("Data: {}", data); } }
运行上面的代码,我们可以看到数据库查询只执行一次,尽管我们使用相同的输入多次调用get_data_from_database函数。这是因为我们缓存结果并返回它,而不是每次都重新计算它,从而节省了执行不必要的昂贵查找的时间。
理解算法的时间复杂度对于编写高效代码至关重要。时间复杂度描述了算法的运行时间如何随着输入大小的增加而增长。通过选择具有更好时间复杂度的算法,可以显著提高代码的性能。
如图: