cargo-perf 0.5.4

Preventive performance analysis for Rust - catch anti-patterns before production
Documentation

cargo-perf

Static analysis for async correctness and runtime performance in Rust.

Crates.io CI License

The Problem

These bugs compile fine and ship to production:

// Blocks the async runtime — causes timeouts under load
async fn read_config() -> Config {
    let data = std::fs::read_to_string("config.toml").unwrap();
    toml::from_str(&data).unwrap()
}

// Deadlock — holds lock across yield point
async fn update(mutex: &tokio::sync::Mutex<Data>) {
    let guard = mutex.lock().await;
    some_async_op().await; // guard still held across await
}

// 737x slower — regex compilation in hot loop
for line in lines {
    if Regex::new(r"\d+").unwrap().is_match(line) { ... }
}

cargo-perf catches all of these.

Installation

cargo install cargo-perf

Usage

cargo perf                          # Analyze current directory
cargo perf --strict                 # High-confidence rules only (CI recommended)
cargo perf --strict --fail-on error # Fail CI on issues
cargo perf --format sarif           # For GitHub Code Scanning
cargo perf fix --dry-run            # Preview auto-fixes
cargo perf fix                      # Apply auto-fixes

Rules

Errors (High Confidence)

Rule What it catches
async-block-in-async std::fs, thread::sleep, blocking I/O in async functions
lock-across-await MutexGuard/RwLockGuard held across .await (deadlock risk)
n-plus-one-query Database queries inside loops (SQLx, Diesel, SeaORM)

Warnings (Medium Confidence)

Rule What it catches Impact
unbounded-channel mpsc::channel(), unbounded_channel() Memory exhaustion
unbounded-spawn tokio::spawn in loops Resource exhaustion
regex-in-loop Regex::new() inside loops 737x slower
clone-in-hot-loop .clone() on heap types in loops 48x slower
collect-then-iterate .collect().iter() 2.3x slower
vec-no-capacity Vec::new() + push in loop 1.8x slower
format-in-loop format!() inside loops Allocates each iteration
string-concat-loop String + in loops Use push_str()
mutex-in-loop Lock acquired inside loop Acquire once outside

CI Integration

# .github/workflows/ci.yml
- name: Performance lint
  run: |
    cargo install cargo-perf
    cargo perf --strict --fail-on error

For a complete workflow with SARIF integration for GitHub Code Scanning, see examples/github-workflow.yml.

Suppressing warnings

// cargo-perf-ignore: clone-in-hot-loop
let owned = data.clone(); // intentional in cold path

Or for a whole function:

#[allow(cargo_perf::clone_in_hot_loop)]
fn cold_path() { ... }

Benchmarks

Real measurements (Apple M1 Pro, 1000 iterations):

Anti-pattern Impact
Regex::new() in loop 737x slower
clone() in loop 48x slower
collect().iter() 2.3x slower
Blocking in async Blocks runtime thread
Lock across await Deadlock

See benchmarks/ for methodology.

IDE Integration

cargo-perf includes an LSP server for real-time diagnostics in your editor.

Installation

cargo install cargo-perf --features lsp

VS Code

See editors/vscode/ for the extension.

Neovim

require('lspconfig.configs').cargo_perf = {
  default_config = {
    cmd = { 'cargo-perf', 'lsp' },
    filetypes = { 'rust' },
    root_dir = require('lspconfig.util').root_pattern('Cargo.toml'),
  },
}
require('lspconfig').cargo_perf.setup({})

Other Editors

See editors/README.md for Emacs, Helix, Zed, and generic LSP setup.

Custom Rules (Plugin System)

Extend cargo-perf with your own rules:

use cargo_perf::plugin::{PluginRegistry, analyze_with_plugins};
use cargo_perf::rules::{Rule, Diagnostic, Severity};

struct MyCustomRule;

impl Rule for MyCustomRule {
    fn id(&self) -> &'static str { "my-rule" }
    fn name(&self) -> &'static str { "My Rule" }
    fn description(&self) -> &'static str { "Detects my anti-pattern" }
    fn default_severity(&self) -> Severity { Severity::Warning }

    fn check(&self, ctx: &AnalysisContext) -> Vec<Diagnostic> {
        // Your detection logic
        Vec::new()
    }
}

let mut registry = PluginRegistry::new();
registry.add_rule(Box::new(MyCustomRule));
let diagnostics = analyze_with_plugins(path, &config, &registry)?;

See examples/custom_rule.rs for a complete example.

License

MIT OR Apache-2.0