rust-perf
Skill0 starsChanged 8 months agoMITView on GitHub
Rust performance optimization — memory, compiler hints, and profiling. Use when optimizing allocations (SmallVec, with_capacity, arena), configuring release profiles (LTO, PGO, codegen-units), adding inline hints, benchmarking with criterion, or profiling with flamegraph. Also use when reviewing code for unnecessary allocations, premature optimization, or format! in hot paths.
In this repository16 other files it gives its agents.
- CLAUDE.mdCLAUDE.md
- rust-apiSkill
- rust-architectureSkill
- rust-asyncSkill
- rust-ci-deploySkill
- rust-databaseSkill
- rust-dddSkill
- rust-errorsSkill
- rust-macrosSkill
- rust-ownershipSkill
- rust-qualitySkill
- rust-serdeSkill
- rust-testsSkill
- rust-tracingSkill
- rust-typesSkill
- rust-unsafeSkill
The file
We split this skill into the rules it gives an agent
1 rule. About 0 are common, 0 are distinctive, and 1 is very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈0
Distinctive · estimate
This author's own choices. Worth a look.
1
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 122
Look for wide bars (time hogs), malloc/free (allocation heavy), memcpy (unnecessary copies).
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Performance Optimization
Core Question
Have you measured first, or are you guessing?
Intuition about performance is often wrong. The code you think is slow frequently isn't. Profile first, then optimize the actual bottleneck with data-driven decisions.
Quick Decisions
| Situation | Reach For | Why |
| Collection size known upfront | Vec::with_capacity(n) | Avoids ~10 reallocations for 1000 elements |
| Usually-small collection (2-8 items) | SmallVec<[T; N]> | Stack-allocated for common case, heap fallback |
| Hard upper bound, no heap allowed | ArrayVec<T, N> | Guaranteed stack-only, panics on overflow |
| Fixed-size heap data, never grows | Box<[T]> / Box<str> | Saves 8 bytes per instance vs Vec/String |
| Often-empty vectors in many instances | ThinVec<T> | 8 bytes empty vs 24 bytes for Vec |
| One enum variant much larger than others | Box the large variant | Clippy large_enum_variant catches this |
| Millions of short strings (< 24 chars) | CompactString | Inline storage, zero heap allocation |
| Repeatedly cloning into same variable | target.clone_from(&source) | Reuses existing allocation |
| Temporary collection in a loop | .clear() + reuse | Keeps capacity across iterations |
format!() in a hot loop | write!(&mut buf, ...) | Zero allocation with reused buffer |
| Many small allocations (AST, parsing) | bumpalo::Bump arena | Bump-pointer allocation, bulk free |
| Parsing without ownership needs | &str / &[u8] slices | Zero-copy, no allocation |
| Map insert-or-update | .entry().or_insert() | Single lookup instead of two |
| Iterating with manual indexing | .iter() / .zip() | Eliminates bounds checks, enables SIMD |
Intermediate .collect() calls | Chain iterators lazily | One allocation, one pass |
| Release build performance | lto = "fat", codegen-units = 1 | 10-20% improvement typical |
| Proven hot inner loop function | #[inline] or #[inline(always)] | Cross-crate inlining hint |
| Error construction path | #[cold] + #[inline(never)] | Keeps cold code out of hot path |
| Need to benchmark correctly | black_box() inputs and outputs | Prevents dead code elimination |
Optimization Priority
Optimize in this order — each level has roughly 10x less impact than the one above:
- Algorithm & data structure — O(n) vs O(n^2) dwarfs everything else
- Data layout — SoA vs AoS, cache-friendly access, avoid pointer chasing
- Allocations — with_capacity, reuse buffers, arena allocators
- Compiler hints — LTO, PGO, codegen-units, inline hints
- SIMD & low-level — portable SIMD, bounds check elimination, target-cpu
Profile BEFORE Optimizing
Tools
| Tool | What It Shows | When to Use |
cargo flamegraph | CPU time by call stack | First step — find where time goes |
cargo instruments -t time (macOS) | CPU time profiling | macOS alternative to perf |
criterion | Micro-benchmark with statistics | Compare before/after for specific functions |
DHAT / heaptrack | Heap allocation sites and counts | When allocation pressure is suspected |
perf stat -e cache-misses | Cache efficiency | When data layout matters |
cargo bloat | Binary size by function/crate | When binary size is a concern |
Workflow
1. Write correct code first
2. Write benchmarks for suspected hot paths
3. Profile under realistic load
4. Identify actual bottlenecks (top 10% of time)
5. Optimize ONE thing
6. Measure improvement with same benchmark
7. Repeat if neededCriterion Quick Setup
use criterion::{black_box, criterion_group, criterion_main, Criterion};
fn bench_hot_function(c: &mut Criterion) {
let data = generate_test_data(1000);
c.bench_function("hot_function", |b| {
b.iter(|| hot_function(black_box(&data)))
});
}
criterion_group!(benches, bench_hot_function);
criterion_main!(benches);Anti-Patterns to Watch For
| Anti-Pattern | Fix |
format!() in hot loop | write!(&mut buffer, ...) with reused buffer |
Intermediate .collect() between iterator steps | Chain lazily, collect once at end |
| Optimizing without profiling data | Run cargo flamegraph first |
#[inline(always)] everywhere | Let compiler decide; use #[inline] for cross-crate |
unsafe to skip bounds checks | Use iterators — they eliminate bounds checks safely |
contains_key() then insert() | Use .entry() API for single lookup |
Usage Scenarios
Scenario 1: "My web handler is slow and I'm not
sure why"
-> Run cargo flamegraph on a representative workload.
Look for wide bars (time hogs), malloc/free
(allocation heavy), memcpy (unnecessary copies).
Optimize only what the flamegraph shows as hot.
Scenario 2: "I have a Vec that I fill in a loop and
it's showing up in profiling"
-> Check if size is known: use with_capacity(). If the
Vec is reused across iterations: .clear() instead of
creating new. If always small (< 8 items): consider
SmallVec. If fixed after creation: convert to
Box<[T]>.
Scenario 3: "I need maximum throughput for a release
binary"
-> Set release profile: lto = "fat",
codegen-units = 1, panic = "abort",
strip = true. For deployment on known hardware:
RUSTFLAGS="-C target-cpu=native". For maximum gains:
use PGO with representative workloads.
Release Profile
[profile.release]
opt-level = 3
lto = "fat"
codegen-units = 1
panic = "abort"
strip = true
[profile.bench]
inherits = "release"
debug = true
strip = false
[profile.dev.package."*"]
opt-level = 3Reference Files
| File | Read When |
| references/memory.md | Allocation strategies: with_capacity, SmallVec, arena, zero-copy, compact strings, type size assertions |
| references/compiler.md | Compiler hints: inline, #[cold], LTO, PGO, codegen-units, target-cpu, SIMD, cache layout |
| references/runtime.md | Runtime patterns: iterators vs indexing, lazy chains, entry API, drain/extend, collect patterns, benchmarking |
Cross-References
| When | Check |
| Clone vs borrow decision for performance | rust-ownership -> Quick Decisions |
| Error construction on cold paths | rust-errors -> Quick Decisions |
| Async runtime and spawn_blocking for CPU work | rust-async -> Quick Decisions |
| API design that avoids unnecessary allocations | rust-api -> Quick Decisions |
| Clippy perf lints and lint configuration | rust-quality -> Quick Decisions |
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

