This crate provides a procedural macro to inject timers into functions and print a warning if it takes longer than expected. It can be particularly useful for debugging performance issues during development.
This is not meant to be a benchmarking tool, but rather a way to detect potential performance issues in your code.
For example my use case was for developing a game in Bevy and I've added this to all my systems to detect if any game system function takes longer than a 1ms.
Add the following to your Cargo.toml
:
[features]
slow_function_warning = ["dep:slow_function_warning"]
[dependencies]
# Add as a feature to avoid affecting the LSP.
slow_function_warning = { version = "0.6.0", optional = true }
# For wasm targets
[target.'cfg(target_family = "wasm")'.dependencies]
web-time = "1"
#[cfg_attr(feature = "slow_function_warning", slow_function_warning(1000ms))] // Warn if the function takes longer than 1000 milliseconds
fn example_function() {
// Function implementation
}
The warning is not on by default and is only recommended for debugging purposes. To enable it use the slow_function_warning
feature.
cargo run --features slow_function_warning
// Warn if the function takes longer than a second with a custom message
#[cfg_attr(feature = "slow_function_warning", slow_function_warning(1ms, println!("Function {function} took too long!")))]
fn example_function() {
// Function implementation
}
You can also use the function parameters in your message:
// Warn if the function takes longer than a second with a custom message
#[cfg_attr(feature = "slow_function_warning", slow_function_warning(1s, println!("Function {function} took {millis} for {} values!", values.len())))]
fn sort(values: &Vec<u32>) {
// Function implementation
}
You can specify the duration using numeric literals followed by a suffix:
ns
for nanosecondsms
for millisecondss
for secondsm
for minutesh
for hoursd
for days
module: String
- The name of the modulefunction: String
- The name of the functionelapsed: Duration
- The elapsed timeelapsed_str: String
- The elapsed time using the limit unit specified (defaults to milliseconds)elapsed_ns: u128
- The elapsed time in nanosecondselapsed_nanos: u128
- The elapsed time in nanosecondselapsed_nanoseconds: u128
- The elapsed time in nanosecondselapsed_us: u128
- The elapsed time in microsecondselapsed_micros: u128
- The elapsed time in microsecondselapsed_microseconds: u128
- The elapsed time in microsecondselapsed_ms: u128
- The elapsed time in millisecondselapsed_millis: u128
- The elapsed time in millisecondselapsed_milliseconds: u128
- The elapsed time in millisecondselapsed_s: u64
- The elapsed time in secondselapsed_secs: u64
- The elapsed time in secondselapsed_seconds: u64
- The elapsed time in secondselapsed_m: u64
- The elapsed time in minuteselapsed_min: u64
- The elapsed time in minuteselapsed_minutes: u64
- The elapsed time in minuteselapsed_h: u64
- The elapsed time in hourselapsed_hours: u64
- The elapsed time in hourselapsed_d: u64
- The elapsed time in dayselapsed_days: u64
- The elapsed time in dayslimit: Duration
- The name of the modulelimit_str: String
- The limit time using the limit unit specified (defaults to milliseconds)limit_ns: u128
- The limit time in nanosecondslimit_nanos: u128
- The limit time in nanosecondslimit_nanoseconds: u128
- The limit time in nanosecondslimit_us: u128
- The limit time in microsecondslimit_micros: u128
- The limit time in microsecondslimit_microseconds: u128
- The limit time in microsecondslimit_ms: u128
- The limit time in millisecondslimit_millis: u128
- The limit time in millisecondslimit_milliseconds: u128
- The limit time in millisecondslimit_s: u64
- The limit time in secondslimit_secs: u64
- The limit time in secondslimit_seconds: u64
- The limit time in secondslimit_m: u64
- The limit time in minuteslimit_min: u64
- The limit time in minuteslimit_minutes: u64
- The limit time in minuteslimit_h: u64
- The limit time in hourslimit_hours: u64
- The limit time in hourslimit_d: u64
- The limit time in dayslimit_days: u64
- The limit time in days
This is a procedural macro that takes the content of a function and places it in a closure, executes it and times how long it took.
// Warn if the function takes longer than a second with a custom message
#[cfg_attr(feature = "slow_function_warning", slow_function_warning(1s, println!("Function {function} took too long!")))]
fn example_function() {
let x = 10;
}
Becomes:
fn example_function() {
let closure = || {
let x = 10;
};
#[cfg(not(target_family = "wasm"))]
let start = std::time::Instant::now();
#[cfg(target_family = "wasm")]
let start = web_time::Instant::now();
let result = closure();
if start.elapsed().as_nanos() > 1000000 {
let module = module_path!();
let function = #function_name;
let elapsed = start.elapsed();
let elapsed_str = #elapsed_str;
let elapsed_ns = elapsed.as_nanos();
let elapsed_nanos = elapsed_ns;
let elapsed_nanoseconds = elapsed_ns;
let elapsed_us = elapsed.as_micros();
let elapsed_micros = elapsed_us;
let elapsed_microseconds = elapsed_us;
let elapsed_ms = elapsed.as_millis();
let elapsed_millis = elapsed_ms;
let elapsed_milliseconds = elapsed_ms;
let elapsed_s = elapsed.as_secs();
let elapsed_secs = elapsed_s;
let elapsed_seconds = elapsed_s;
let elapsed_m = elapsed.as_secs() / 60;
let elapsed_min = elapsed_m;
let elapsed_minutes = elapsed_m;
let elapsed_h = elapsed.as_secs() / 60 / 60;
let elapsed_hours = elapsed_h;
let elapsed_d = elapsed.as_secs() / 60 / 60 / 24;
let elapsed_days = elapsed_d;
let limit = Duration::from_nanos(#nano_seconds as u64);
let limit_str = #limit_str;
let limit_ns = limit.as_nanos();
let limit_nanos = limit_ns;
let limit_nanoseconds = limit_ns;
let limit_us = limit.as_micros();
let limit_micros = limit_us;
let limit_microseconds = limit_us;
let limit_ms = limit.as_millis();
let limit_millis = limit_ms;
let limit_milliseconds = limit_ms;
let limit_s = limit.as_secs();
let limit_secs = limit_s;
let limit_seconds = limit_s;
let limit_m = limit.as_secs() / 60;
let limit_min = limit_m;
let limit_minutes = limit_m;
let limit_h = limit.as_secs() / 60 / 60;
let limit_hours = limit_h;
let limit_d = limit.as_secs() / 60 / 60 / 24;
let limit_days = limit_d;
println!("Warning: {module}::{function}: ran for {elapsed_str} (limit: {limit_str})")
}
result
}