| name | rust-performance |
| description | Optimize Rust code for performance using profiling, benchmarking, and known optimization patterns. Use when asked to improve performance, reduce allocations, optimize hot paths, write benchmarks, or profile code. Covers criterion benchmarks, allocation tracking, and common Rust performance patterns. |
| allowed-tools | Read, Edit, Write, Bash, Grep, Glob |
Rust Performance Optimization
Identify and fix performance issues in cqlsh-rs using profiling, benchmarking, and idiomatic optimization patterns.
Workflow
- Measure first — Profile or benchmark before optimizing
- Identify bottleneck — Find the actual hot path
- Apply targeted fix — Optimize the bottleneck, not everything
- Verify improvement — Benchmark before and after
Benchmarking with Criterion
The project uses criterion for benchmarks. See docs/plans/11-benchmarking.md for the benchmarking plan.
Creating a benchmark
use criterion::{criterion_group, criterion_main, Criterion, black_box};
fn bench_format_row(c: &mut Criterion) {
let row = create_test_row();
c.bench_function("format_row_10_columns", |b| {
b.iter(|| format_row(black_box(&row)))
});
}
fn bench_format_row_group(c: &mut Criterion) {
let mut group = c.benchmark_group("row_formatting");
for size in [1, 10, 50, 100] {
let row = create_test_row_with_columns(size);
group.bench_with_input(
BenchmarkId::from_parameter(size),
&row,
|b, row| b.iter(|| format_row(black_box(row))),
);
}
group.finish();
}
criterion_group!(benches, bench_format_row, bench_format_row_group);
criterion_main!(benches);
Running benchmarks
cargo bench
cargo bench --bench formatting
cargo bench -- --save-baseline before
cargo bench -- --baseline before
Common Optimization Patterns
1. Reduce allocations
fn format_value(val: &CqlValue) -> String {
format!("{}", val)
}
fn format_value(val: &CqlValue, buf: &mut String) {
use std::fmt::Write;
write!(buf, "{}", val).unwrap();
}
2. Pre-allocate collections
let mut results = Vec::new();
for row in rows {
results.push(process(row));
}
let mut results = Vec::with_capacity(rows.len());
for row in rows {
results.push(process(row));
}
let results: Vec<_> = rows.iter().map(process).collect();
3. Avoid unnecessary String allocation
if column.name().to_string() == "key" { ... }
if column.name() == "key" { ... }
use std::borrow::Cow;
fn normalize_name(name: &str) -> Cow<'_, str> {
if name.contains(' ') {
Cow::Owned(name.replace(' ', "_"))
} else {
Cow::Borrowed(name)
}
}
4. Use SmallVec for small collections
use smallvec::SmallVec;
type Columns = SmallVec<[Column; 8]>;
5. Avoid redundant work in loops
for line in lines {
let re = Regex::new(r"^\s*--").unwrap();
if re.is_match(line) { ... }
}
let re = Regex::new(r"^\s*--").unwrap();
for line in lines {
if re.is_match(line) { ... }
}
static COMMENT_RE: LazyLock<Regex> = LazyLock::new(|| {
Regex::new(r"^\s*--").unwrap()
});
6. Efficient string building
let mut output = String::new();
for col in columns {
output = output + &col.name + " | ";
}
use std::fmt::Write;
let mut output = String::with_capacity(columns.len() * 20);
for col in columns {
write!(output, "{} | ", col.name).unwrap();
}
7. Use bytes() for ASCII processing
let count = text.chars().filter(|c| *c == '\n').count();
let count = text.bytes().filter(|b| *b == b'\n').count();
let count = memchr::memchr_iter(b'\n', text.as_bytes()).count();
8. Async performance
let a = fetch_a().await?;
let b = fetch_b().await?;
let (a, b) = tokio::try_join!(fetch_a(), fetch_b())?;
let guard = mutex.lock().await;
do_async_work().await;
drop(guard);
let data = {
let guard = mutex.lock().await;
guard.clone()
};
do_async_work_with(data).await;
Profiling Tools
Memory profiling
cargo install dhat
cargo run --features dhat-heap -- [args]
MALLOC_CONF=prof:true,prof_prefix:jeprof cargo run -- [args]
CPU profiling
cargo build --release
perf record --call-graph dwarf ./target/release/cqlsh [args]
perf report
cargo install flamegraph
cargo flamegraph -- [args]
Compile time profiling
cargo build --timings
Performance Checklist