XPerf
LanguagesRust

Rust performance guide (v0)

Less allocator chatter, fewer surprise string / print stalls.

Scope: Rust edition 2021+. Speedup claims cite a local bench, an upstream source, or are marked provisional. Corpus residual counts are lint hit counts, not wall-time savings.

Profile first (cargo bench, criterion, perf). LLVM optimizations change costs — re-bench after toolchain bumps.

Contents

1. Allocation and clone

Prefer reuse and capacity over Vec::new / .clone() / .to_string() inside hot loops when profiles show pressure.

// prefer: pre-sized or reused buffer
let mut v = Vec::with_capacity(n);
for i in 0..n { v.push(i); }

Lint: rs-alloc-hot (default off after G3; G4 measured).

2. Strings and hot I/O

format!("{s}…") / s = format!(…) accumulation in a loop is alloc-heavy (copies the growing prefix). Prefer with_capacity + write!, or collect then join. Plain += / push_str on one growing String was about 1× versus with_capacity on a large fill, so that lint was removed. Keep println! / eprintln! out of tight loops (gate or buffer).

A large Vec::new plus push was about 1× versus with_capacity, so that lint was removed. Avoid .collect::<Vec<_>>() inside per-item loops when a reused buffer or streaming count will do.

Lint: rs-format-rebind-hot (default error; G4: ~99× vs write!); rs-println-hot, rs-collect-vec-hot (default hint).

3. Regex

Regex::new inside a loop recompiles the pattern. On regex 1.13.1 and rustc 1.99, constructing every iteration and matching a short haystack was about 963× versus constructing once (both sides still match). This lint is hint. HashMap::new plus one insert, 20000 times, was about 1.5× versus clearing one map on two quiet rustc 1.99 runs (rs-map-new-hot, hint).

File::open of one path was about 416× on rustc 1.99 versus opening once. Instant::now was about 20× on rustc 1.99 versus one sample. A changing path cannot be hoisted, and a loop that needs a fresh timestamp has nothing to hoist.

Lint: rs-regex-new-hot, rs-file-open-hot, rs-clock-hot (default hint).

Lint map

IDSmellDefault
rs-alloc-hotalloc/clone in loopoff (G3 OK-idiom; G4 measured)
rs-println-hotprintln! / eprintln! in loophint
rs-format-rebind-hotformat! rebind accum in looperror (G4: ~99× vs write!)
rs-collect-vec-hotcollect::<Vec> in loophint
rs-regex-new-hotRegex::new in loophint (JSONL about 963× on a short haystack)

See evidence-policy.md. Demote rs-format-rebind-hot with --severity rs-format-rebind-hot=hint if one-shot wraps dominate. Amortized += and large Vec::new plus push were removed.

References