agentleFS
dawidpereiradawidpereira/rust-skills/skills/rust-perf/SKILL.md

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.

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
  1. Very specific:

    Look for wide bars (time hogs), malloc/free (allocation heavy), memcpy (unnecessary copies).

    line 122

≈ “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.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

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

SituationReach ForWhy
Collection size known upfrontVec::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 allowedArrayVec<T, N>Guaranteed stack-only, panics on overflow
Fixed-size heap data, never growsBox<[T]> / Box<str>Saves 8 bytes per instance vs Vec/String
Often-empty vectors in many instancesThinVec<T>8 bytes empty vs 24 bytes for Vec
One enum variant much larger than othersBox the large variantClippy large_enum_variant catches this
Millions of short strings (< 24 chars)CompactStringInline storage, zero heap allocation
Repeatedly cloning into same variabletarget.clone_from(&source)Reuses existing allocation
Temporary collection in a loop.clear() + reuseKeeps capacity across iterations
format!() in a hot loopwrite!(&mut buf, ...)Zero allocation with reused buffer
Many small allocations (AST, parsing)bumpalo::Bump arenaBump-pointer allocation, bulk free
Parsing without ownership needs&str / &[u8] slicesZero-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() callsChain iterators lazilyOne allocation, one pass
Release build performancelto = "fat", codegen-units = 110-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 correctlyblack_box() inputs and outputsPrevents dead code elimination

Optimization Priority

Optimize in this order — each level has roughly 10x less impact than the one above:

  1. Algorithm & data structure — O(n) vs O(n^2) dwarfs everything else
  2. Data layout — SoA vs AoS, cache-friendly access, avoid pointer chasing
  3. Allocations — with_capacity, reuse buffers, arena allocators
  4. Compiler hints — LTO, PGO, codegen-units, inline hints
  5. SIMD & low-level — portable SIMD, bounds check elimination, target-cpu

Profile BEFORE Optimizing

Tools

ToolWhat It ShowsWhen to Use
cargo flamegraphCPU time by call stackFirst step — find where time goes
cargo instruments -t time (macOS)CPU time profilingmacOS alternative to perf
criterionMicro-benchmark with statisticsCompare before/after for specific functions
DHAT / heaptrackHeap allocation sites and countsWhen allocation pressure is suspected
perf stat -e cache-missesCache efficiencyWhen data layout matters
cargo bloatBinary size by function/crateWhen 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 needed

Criterion 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-PatternFix
format!() in hot loopwrite!(&mut buffer, ...) with reused buffer
Intermediate .collect() between iterator stepsChain lazily, collect once at end
Optimizing without profiling dataRun cargo flamegraph first
#[inline(always)] everywhereLet compiler decide; use #[inline] for cross-crate
unsafe to skip bounds checksUse 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 = 3

Reference Files

FileRead When
references/memory.mdAllocation strategies: with_capacity, SmallVec, arena, zero-copy, compact strings, type size assertions
references/compiler.mdCompiler hints: inline, #[cold], LTO, PGO, codegen-units, target-cpu, SIMD, cache layout
references/runtime.mdRuntime patterns: iterators vs indexing, lazy chains, entry API, drain/extend, collect patterns, benchmarking

Cross-References

WhenCheck
Clone vs borrow decision for performancerust-ownership -> Quick Decisions
Error construction on cold pathsrust-errors -> Quick Decisions
Async runtime and spawn_blocking for CPU workrust-async -> Quick Decisions
API design that avoids unnecessary allocationsrust-api -> Quick Decisions
Clippy perf lints and lint configurationrust-quality -> Quick Decisions

Download now (ZIP)

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
What did you use it for? e.g. Reviewing pull requests in a Rails app

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.