Rust性能分析工具logging_timer的使用:高效代码计时与日志记录库

Rust性能分析工具logging_timer的使用:高效代码计时与日志记录库

使用计时器属性

最简单的方法是使用timestime属性来检测函数,函数名将用作计时器的名称:

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在这里被删除但不产生消息

设置日志级别

默认情况下,timerstimer都记录在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

性能说明

timerstimer宏返回一个Option<LoggingTimer>。使用log_enabled方法检查是否在请求的级别启用了日志记录。如果未启用日志记录,则返回None。这样可以避免在计时器无效时的大多数计算。

在64位Linux上,Option<LoggingTimer>的大小为104字节。


1 回复

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

注意事项

  1. 确保你的项目已经初始化了日志系统(如env_logger
  2. 计时器变量(如_t)必须保持作用域直到代码块结束
  3. 对于生产环境,考虑使用stime!宏来避免发布构建中的性能开销

logging_timer是一个轻量级但功能强大的工具,可以帮助你快速识别性能瓶颈,是Rust性能优化的好帮手。

回到顶部