Rust性能分析工具logging_timer的使用:高效代码计时与日志记录库
Rust性能分析工具logging_timer的使用:高效代码计时与日志记录库
使用计时器属性
最简单的方法是使用time
或stime
属性来检测函数,函数名将用作计时器的名称:
use logging_timer::{time, stime};
#[time]
fn find_files(dir: PathBuf) -> Vec<PathBuf> {
let files = vec![];
// 这里执行耗时操作
return files;
} // 这里会记录'TimerFinished'消息
这两个属性都接受两个可选参数,用于指定日志级别和模式(将函数名代入其中)。当你在同一模块中有许多同名函数时,模式很有帮助:
#[time] // 使用默认的Debug日志级别
#[time("info")] // 设置自定义日志级别
#[time("info", "FirstStruct::{}")] // 在Info级别记录"FirstStruct::new()"
#[time("info", "SecondStruct::{}")] // 在Info级别记录"SecondStruct::new()"
#[time("ThirdStruct::{}")] // 在Debug级别记录"ThirdStruct::new()"
#[time("never")] // 在编译时完全关闭检测
使用内联计时器
提供更多灵活性(包括记录额外信息)的是两个类似函数的宏形式:timer!
和stimer!
。区别在于timer!
返回一个计时器,仅在删除时记录消息,而stimer!
返回一个计时器,在创建时立即记录开始消息,在删除时记录完成消息。
在这个例子中,"FIND_FILES"是计时器的名称:
use logging_timer::{timer};
fn find_files(dir: PathBuf) -> Vec<PathBuf> {
let _tmr = timer!("FIND_FILES");
let files = vec![];
// 这里执行耗时操作
return files;
} // _tmr在这里被删除,并记录'TimerFinished'消息
你可以用stimer!
替换timer!
来获得一个同时记录开始消息的计时器。
此外,两个计时器宏都接受format_args!
风格的参数,允许你在日志消息中包含额外信息:
let _tmr = timer!("FIND_FILES", "Directory = {}", dir);
输出中间消息
executing!
宏允许你让计时器在被删除之前产生消息。你可以多次调用它:
use logging_timer::{timer, executing};
fn find_files(dir: PathBuf) -> Vec<PathBuf> {
let tmr = timer!("FIND_FILES");
let files = vec![];
for dir in sub_dirs(dir) {
// 耗时操作
executing!(tmr, "Processed {}", dir);
}
return files;
} // tmr在这里被删除,并记录'TimerFinished'消息
控制最终消息
finish!
宏也会让计时器记录一条消息,但它还有抑制正常删除消息的副作用:
use logging_timer::{timer, executing, finish};
fn find_files(dir: PathBuf) -> Vec<PathBuf> {
let tmr = timer!("FIND_FILES");
let files = vec![];
finish!(tmr, "Found {} files", files.len());
return files;
} // tmr在这里被删除但不产生消息
设置日志级别
默认情况下,timer
和stimer
都记录在Debug
级别。这些宏的可选第一个参数允许你设置日志级别:
let tmr1 = timer!(Level::Warn; "TIMER_AT_WARN");
let tmr2 = stimer!(Level::Info; "TIMER_AT_INFO");
完整示例代码
use logging_timer::{time, timer, stimer, executing, finish};
use std::path::PathBuf;
use std::thread;
use std::time::Duration;
// 使用time属性自动计时
#[time("info", "文件查找::{}")]
fn find_files_with_attr(dir: PathBuf) -> Vec<PathBuf> {
thread::sleep(Duration::from_millis(100));
vec![]
}
// 使用timer宏手动计时
fn find_files_with_timer(dir: PathBuf) -> Vec<PathBuf> {
let _tmr = timer!("FIND_FILES_WITH_TIMER", "dir={}", dir.display());
thread::sleep(Duration::from_millis(150));
vec![]
}
// 使用stimer宏记录开始和结束时间
fn find_files_with_stimer(dir: PathBuf) -> Vec<PathBuf> {
let tmr = stimer!("FIND_FILES_WITH_STIMER");
thread::sleep(Duration::from_millis(200));
finish!(tmr, "Finished processing {}", dir.display());
vec![]
}
// 使用executing宏记录中间过程
fn process_files(files: Vec<PathBuf>) {
let tmr = timer!("PROCESS_FILES");
for file in files {
thread::sleep(Duration::from_millis(50);
executing!(tmr, "Processed {}", file.display());
}
}
fn main() {
// 初始化日志
env_logger::init();
let dir = PathBuf::from("/path/to/files");
// 测试各种计时方法
let _ = find_files_with_attr(dir.clone());
let _ = find_files_with_timer(dir.clone());
let _ = find_files_with_stimer(dir.clone());
process_files(vec![
PathBuf::from("file1.txt"),
PathBuf::from("file2.txt"),
PathBuf::from("file3.txt")
]);
}
要运行此示例,请确保在Cargo.toml中添加了依赖项:
[dependencies]
logging_timer = "1.1.1"
env_logger = "0.9"
运行程序时设置日志级别为debug:
RUST_LOG=debug cargo run
性能说明
timer
和stimer
宏返回一个Option<LoggingTimer>
。使用log_enabled
方法检查是否在请求的级别启用了日志记录。如果未启用日志记录,则返回None
。这样可以避免在计时器无效时的大多数计算。
在64位Linux上,Option<LoggingTimer>
的大小为104字节。
Rust性能分析工具logging_timer的使用:高效代码计时与日志记录库
logging_timer
是一个简单易用的Rust库,用于测量代码块的执行时间并自动记录日志。它特别适合性能分析和优化工作。
安装
在Cargo.toml
中添加依赖:
[dependencies]
logging_timer = "1.0"
基本用法
1. 最简单的计时器
use logging_timer::time;
fn my_function() {
// 自动记录"my_function"的执行时间
let _t = time!("my_function");
// 你的代码逻辑...
std::thread::sleep(std::time::Duration::from_millis(100));
}
2. 带自定义消息的计时器
use logging_timer::time;
fn process_data(data: &str) {
let _t = time!("process_data"; "Processing data: {}", data);
// 处理数据的代码...
std::thread::sleep(std::time::Duration::from_millis(50));
}
3. 嵌套计时器
use logging_timer::time;
fn complex_operation() {
let _t1 = time!("complex_operation");
{
let _t2 = time!("sub_operation_1");
std::thread::sleep(std::time::Duration::from_millis(30));
}
{
let _t3 = time!("sub_operation_2");
std::thread::sleep(std::time::Duration::from_millis(20));
}
}
高级功能
1. 自定义日志级别
use logging_timer::{time, Level};
use log::LevelFilter;
fn important_function() {
// 使用WARN级别记录耗时
let _t = time!(Level::Warn, "important_function");
// 关键业务代码...
std::thread::sleep(std::time::Duration::from_millis(200));
}
2. 条件编译
use logging_timer::stime;
fn release_mode_function() {
// 只在debug构建时计时
let _t = stime!("release_mode_function");
// 生产环境代码...
std::thread::sleep(std::time::Duration::from_millis(150));
}
3. 获取计时结果
use logging_timer::Timer;
fn get_elapsed_time() -> u128 {
let timer = Timer::new("get_elapsed_time");
// 执行一些操作...
std::thread::sleep(std::time::Duration::from_millis(75));
// 手动获取耗时(毫秒)
timer.elapsed().as_millis()
}
配置选项
你可以在项目初始化时配置全局选项:
use logging_timer::Builder;
fn main() {
Builder::new()
.with_level(log::Level::Info) // 默认日志级别
.with_prefix("[TIMER] ") // 自定义前缀
.init();
// 你的应用代码...
}
完整示例代码
下面是一个完整的示例,展示如何使用logging_timer
来分析一个简单的Web请求处理流程:
use logging_timer::{time, stime, Level, Builder};
use log::{info, LevelFilter};
use env_logger::Builder as EnvBuilder;
// 初始化日志系统
fn init_logger() {
EnvBuilder::new()
.filter_level(LevelFilter::Info)
.init();
Builder::new()
.with_level(log::Level::Info)
.with_prefix("[PERF] ")
.init();
}
// 模拟从数据库获取用户数据
fn fetch_user_from_db(user_id: u64) -> String {
let _t = time!("fetch_user_from_db");
// 模拟数据库查询延迟
std::thread::sleep(std::time::Duration::from_millis(80));
format!("User data for {}", user_id)
}
// 处理用户数据
fn process_user_data(data: &str) -> String {
let _t = time!("process_user_data");
// 模拟数据处理延迟
std::thread::sleep(std::time::Duration::from_millis(120));
data.to_uppercase()
}
// 只在debug构建中计时的辅助函数
fn debug_helper() {
let _t = stime!("debug_helper");
// 只在debug模式下计时的代码
std::thread::sleep(std::time::Duration::from_millis(30));
}
// 关键业务函数 - 使用高级别日志
fn critical_business_logic() {
let _t = time!(Level::Warn, "critical_business_logic");
// 模拟关键业务处理
std::thread::sleep(std::time::Duration::from_millis(150));
}
// 主请求处理函数
fn handle_request(user_id: u64) -> String {
let _t = time!("handle_request"; "User ID: {}", user_id);
debug_helper();
let user_data = fetch_user_from_db(user_id);
let processed_data = process_user_data(&user_data);
critical_business_logic();
processed_data
}
fn main() {
init_logger();
info!("Application started");
let result = handle_request(42);
info!("Request processed: {}", result);
}
日志输出示例
运行上述代码后,你可能会看到类似这样的日志输出:
[INFO] Application started
[INFO] [PERF] Starting 'handle_request' (User ID: 42)
[INFO] [PERF] Starting 'fetch_user_from_db'
[INFO] [PERF] 'fetch_user_from_db' took 82.456ms
[INFO] [PERF] Starting 'process_user_data'
[INFO] [PERF] 'process_user_data' took 122.123ms
[WARN] [PERF] Starting 'critical_business_logic'
[WARN] [PERF] 'critical_business_logic' took 152.789ms
[INFO] [PERF] 'handle_request' took 357.368ms
[INFO] Request processed: USER DATA FOR 42
注意事项
- 确保你的项目已经初始化了日志系统(如
env_logger
) - 计时器变量(如
_t
)必须保持作用域直到代码块结束 - 对于生产环境,考虑使用
stime!
宏来避免发布构建中的性能开销
logging_timer
是一个轻量级但功能强大的工具,可以帮助你快速识别性能瓶颈,是Rust性能优化的好帮手。