Performance Optimizer
Evidence-based performance optimization. Never optimize without profiling first. Every optimization comes with a trade-off analysis.
Quick Start
Investigate a Performance Issue
- Reproduce: Can you reliably reproduce the slowness? What are the conditions?
- Measure: Profile to find the actual bottleneck. Don't guess.
- Analyze: What's the root cause? Algorithm? I/O? Memory? Contention?
- Propose: 2-3 options with trade-offs (speed vs readability, memory vs CPU, etc.)
- Verify: After optimization, re-measure to confirm improvement.
The cardinal rule: Profile first, optimize second. Gut feelings about performance are wrong more often than right.
Profiling Toolkits
Rust
| Tool |
Purpose |
Command |
cargo bench |
Micro-benchmarks (criterion) |
cargo bench |
cargo flamegraph |
CPU flamegraph visualization |
cargo flamegraph --bin <name> |
perf |
Linux perf events |
perf record --call-graph dwarf ./target/release/<bin> |
valgrind --tool=callgrind |
Instruction-level profiling |
valgrind --tool=callgrind ./target/release/<bin> |
cargo bloat |
Binary size analysis |
cargo bloat --release |
| DHAT (via valgrind) |
Heap allocation profiling |
valgrind --tool=dhat ./target/release/<bin> |
Quick benchmark setup with criterion:
// benches/my_bench.rs
use criterion::{criterion_group, criterion_main, Criterion};
fn benchmark_function(c: &mut Criterion) {
c.bench_function("descriptive name", |b| {
b.iter(|| {
// code to benchmark
})
});
}
criterion_group!(benches, benchmark_function);
criterion_main!(benches);
TypeScript / React
| Tool |
Purpose |
How |
| React DevTools Profiler |
Component render timing |
Browser extension, Profiler tab |
| Lighthouse |
Overall web performance |
Chrome DevTools > Lighthouse |
console.time() / console.timeEnd() |
Quick timing |
Wrap suspicious code |
| webpack-bundle-analyzer |
Bundle size analysis |
bun run build --analyze |
performance.mark() / performance.measure() |
Web Performance API |
Precise timing of code sections |
React <Profiler> component |
Programmatic render profiling |
Wrap components in <Profiler> |
Key React performance checks:
- Unnecessary re-renders (use React DevTools "Highlight updates")
- Large component trees re-rendering from state at the top
- Missing
useMemo / useCallback for expensive computations or stable references
- Bundle size — are you importing entire libraries for one function?
Django / Python
| Tool |
Purpose |
Command |
django-debug-toolbar |
SQL queries, template timing |
Add to INSTALLED_APPS |
cProfile |
Function-level profiling |
python -m cProfile -s cumtime manage.py <command> |
py-spy |
Sampling profiler (no code changes) |
py-spy record -o profile.svg -- python manage.py runserver |
silk |
Django request/response profiling |
Add to middleware |
memory_profiler |
Line-by-line memory usage |
@profile decorator |
django.db.connection.queries |
Raw SQL query log |
from django.db import connection; print(connection.queries) |
Key Django performance checks:
- N+1 queries (use
django-debug-toolbar or assertNumQueries in tests)
- Missing database indexes on filtered/ordered columns
- Unoptimized querysets (use
.only(), .defer(), .values() when appropriate)
- Template rendering time (are you doing computation in templates?)
Complexity Analysis
Big-O Quick Reference
| Complexity |
Name |
Example |
Scale |
| O(1) |
Constant |
HashMap lookup |
Handles any size |
| O(log n) |
Logarithmic |
Binary search |
Handles billions |
| O(n) |
Linear |
Single pass over collection |
Handles millions |
| O(n log n) |
Linearithmic |
Good sorting (merge, heap) |
Handles millions |
| O(n^2) |
Quadratic |
Nested loops over same collection |
Handles thousands |
| O(n^3) |
Cubic |
Triple nested loops |
Handles hundreds |
| O(2^n) |
Exponential |
Brute-force subsets |
Handles ~25 |
When to Care
- O(1) to O(n): Almost never a problem. Don't optimize.
- O(n log n): Fine for most use cases. Only optimize for very hot paths.
- O(n^2): Watch the input size. Fine for n < 1000, problematic for n > 10000.
- O(n^3) or worse: Red flag. Look for algorithmic improvements first.
Cyclomatic and Data Flow Complexity
Per the project coding standards:
- Cyclomatic complexity: Must be < 25 per function
- Data flow complexity: Must be < 25 per function
When complexity exceeds these thresholds, the refactoring-advisor skill should be used to plan decomposition.
Common Optimization Patterns
Rust-Specific
| Pattern |
When |
Trade-off |
Replace Vec<Box<dyn Trait>> with enum dispatch |
Known, finite set of variants |
Less flexible, but no heap allocation per item |
Use &str instead of String |
Function doesn't need ownership |
Lifetime annotations may complicate API |
Pre-allocate with Vec::with_capacity() |
Known or estimated collection size |
Minor memory overhead if estimate is wrong |
Use SmallVec |
Usually-small collections |
Extra dependency, more complex type |
Replace clone() with borrows |
Cloning on hot paths |
More lifetime management |
Use rayon for data parallelism |
CPU-bound work on large collections |
Thread pool overhead for small collections |
React/TypeScript-Specific
| Pattern |
When |
Trade-off |
React.memo() |
Component re-renders with same props |
Extra memory for memoized result, stale risk if deps wrong |
useMemo / useCallback |
Expensive computation or stable reference needed |
Complexity, memory for cache |
Code splitting with React.lazy() |
Large bundles, route-level splitting |
Loading states, waterfall risk |
Virtualized lists (react-window) |
Rendering 100+ items in a list |
More complex implementation |
| Debounce/throttle |
Frequent events (scroll, resize, input) |
Delayed response |
Django/Python-Specific
| Pattern |
When |
Trade-off |
select_related() |
ForeignKey lookups in loops |
Larger initial query, but fewer total queries |
prefetch_related() |
Reverse FK / M2M lookups in loops |
Extra query, but bounded number of queries |
.values() / .values_list() |
Only need specific columns |
Lose model instance methods |
| Database indexes |
Frequently filtered/ordered columns |
Slower writes, disk space |
Caching (django.core.cache) |
Expensive queries repeated often |
Stale data risk, cache invalidation complexity |
| Bulk operations |
Creating/updating many rows |
Less granular error handling |
Trade-Off Analysis Template
When recommending an optimization, always present:
### Optimization: [Description]
**Current:** [What's happening now, with measured performance]
**Proposed:** [What to change]
| Dimension | Before | After |
|---|---|---|
| Time complexity | O(n^2) | O(n log n) |
| Space complexity | O(1) | O(n) |
| Readability | Simple nested loop | Sort + scan requires comment |
| Maintainability | Easy to modify | Requires understanding of invariant |
**Recommendation:** [Proceed / Defer / Skip]
**Justification:** [Why this trade-off is or isn't worth it in this context]
Anti-Patterns: When NOT to Optimize
- Premature optimization. If there's no measured performance problem, don't create complexity to solve one.
- Micro-optimizing cold paths. That function called once at startup? Leave it readable.
- Optimizing without benchmarks. "I think this is slow" is not evidence. Profile first.
- Sacrificing correctness for speed. Fast and wrong is worse than slow and right.
- Cargo-culting. "I read that HashMap is faster" — depends on the size, access pattern, and key type. Measure in your context.
Integration
- Data: Performance findings logged to
MEMORY.md
- Tools: Can run profiling/benchmarking tools without asking (see execution policies in AGENTS.md)
- Refactoring: When optimization requires restructuring, use the refactoring-advisor skill for safe migration planning
- Trade-offs: Every optimization recommendation includes a trade-off table
Arc skill — Performance profiling and optimization
1---2name: performance-optimizer3description: Agents should invoke this skill for slow code, high CPU/memory, latency, large data processing, algorithmic complexity, profiling plans, benchmarks, or optimization requests. Profiles first and weighs trade-offs before changing code.4---56# Performance Optimizer78Evidence-based performance optimization. Never optimize without profiling first. Every optimization comes with a trade-off analysis.910## Quick Start1112### Investigate a Performance Issue13141. **Reproduce:** Can you reliably reproduce the slowness? What are the conditions?152. **Measure:** Profile to find the actual bottleneck. Don't guess.163. **Analyze:** What's the root cause? Algorithm? I/O? Memory? Contention?174. **Propose:** 2-3 options with trade-offs (speed vs readability, memory vs CPU, etc.)185. **Verify:** After optimization, re-measure to confirm improvement.1920**The cardinal rule:** Profile first, optimize second. Gut feelings about performance are wrong more often than right.2122---2324## Profiling Toolkits2526### Rust2728| Tool | Purpose | Command |29|---|---|---|30| `cargo bench` | Micro-benchmarks (criterion) | `cargo bench` |31| `cargo flamegraph` | CPU flamegraph visualization | `cargo flamegraph --bin <name>` |32| `perf` | Linux perf events | `perf record --call-graph dwarf ./target/release/<bin>` |33| `valgrind --tool=callgrind` | Instruction-level profiling | `valgrind --tool=callgrind ./target/release/<bin>` |34| `cargo bloat` | Binary size analysis | `cargo bloat --release` |35| DHAT (via valgrind) | Heap allocation profiling | `valgrind --tool=dhat ./target/release/<bin>` |3637**Quick benchmark setup with criterion:**3839```rust40// benches/my_bench.rs41use criterion::{criterion_group, criterion_main, Criterion};4243fn benchmark_function(c: &mut Criterion) {44 c.bench_function("descriptive name", |b| {45 b.iter(|| {46 // code to benchmark47 })48 });49}5051criterion_group!(benches, benchmark_function);52criterion_main!(benches);53```5455### TypeScript / React5657| Tool | Purpose | How |58|---|---|---|59| React DevTools Profiler | Component render timing | Browser extension, Profiler tab |60| Lighthouse | Overall web performance | Chrome DevTools > Lighthouse |61| `console.time()` / `console.timeEnd()` | Quick timing | Wrap suspicious code |62| webpack-bundle-analyzer | Bundle size analysis | `bun run build --analyze` |63| `performance.mark()` / `performance.measure()` | Web Performance API | Precise timing of code sections |64| React `<Profiler>` component | Programmatic render profiling | Wrap components in `<Profiler>` |6566**Key React performance checks:**67- Unnecessary re-renders (use React DevTools "Highlight updates")68- Large component trees re-rendering from state at the top69- Missing `useMemo` / `useCallback` for expensive computations or stable references70- Bundle size — are you importing entire libraries for one function?7172### Django / Python7374| Tool | Purpose | Command |75|---|---|---|76| `django-debug-toolbar` | SQL queries, template timing | Add to INSTALLED_APPS |77| `cProfile` | Function-level profiling | `python -m cProfile -s cumtime manage.py <command>` |78| `py-spy` | Sampling profiler (no code changes) | `py-spy record -o profile.svg -- python manage.py runserver` |79| `silk` | Django request/response profiling | Add to middleware |80| `memory_profiler` | Line-by-line memory usage | `@profile` decorator |81| `django.db.connection.queries` | Raw SQL query log | `from django.db import connection; print(connection.queries)` |8283**Key Django performance checks:**84- N+1 queries (use `django-debug-toolbar` or `assertNumQueries` in tests)85- Missing database indexes on filtered/ordered columns86- Unoptimized querysets (use `.only()`, `.defer()`, `.values()` when appropriate)87- Template rendering time (are you doing computation in templates?)8889---9091## Complexity Analysis9293### Big-O Quick Reference9495| Complexity | Name | Example | Scale |96|---|---|---|---|97| O(1) | Constant | HashMap lookup | Handles any size |98| O(log n) | Logarithmic | Binary search | Handles billions |99| O(n) | Linear | Single pass over collection | Handles millions |100| O(n log n) | Linearithmic | Good sorting (merge, heap) | Handles millions |101| O(n^2) | Quadratic | Nested loops over same collection | Handles thousands |102| O(n^3) | Cubic | Triple nested loops | Handles hundreds |103| O(2^n) | Exponential | Brute-force subsets | Handles ~25 |104105### When to Care106107- **O(1) to O(n):** Almost never a problem. Don't optimize.108- **O(n log n):** Fine for most use cases. Only optimize for very hot paths.109- **O(n^2):** Watch the input size. Fine for n < 1000, problematic for n > 10000.110- **O(n^3) or worse:** Red flag. Look for algorithmic improvements first.111112### Cyclomatic and Data Flow Complexity113114Per the project coding standards:115116- **Cyclomatic complexity:** Must be < 25 per function117- **Data flow complexity:** Must be < 25 per function118119When complexity exceeds these thresholds, the refactoring-advisor skill should be used to plan decomposition.120121---122123## Common Optimization Patterns124125### Rust-Specific126127| Pattern | When | Trade-off |128|---|---|---|129| Replace `Vec<Box<dyn Trait>>` with enum dispatch | Known, finite set of variants | Less flexible, but no heap allocation per item |130| Use `&str` instead of `String` | Function doesn't need ownership | Lifetime annotations may complicate API |131| Pre-allocate with `Vec::with_capacity()` | Known or estimated collection size | Minor memory overhead if estimate is wrong |132| Use `SmallVec` | Usually-small collections | Extra dependency, more complex type |133| Replace `clone()` with borrows | Cloning on hot paths | More lifetime management |134| Use `rayon` for data parallelism | CPU-bound work on large collections | Thread pool overhead for small collections |135136### React/TypeScript-Specific137138| Pattern | When | Trade-off |139|---|---|---|140| `React.memo()` | Component re-renders with same props | Extra memory for memoized result, stale risk if deps wrong |141| `useMemo` / `useCallback` | Expensive computation or stable reference needed | Complexity, memory for cache |142| Code splitting with `React.lazy()` | Large bundles, route-level splitting | Loading states, waterfall risk |143| Virtualized lists (`react-window`) | Rendering 100+ items in a list | More complex implementation |144| Debounce/throttle | Frequent events (scroll, resize, input) | Delayed response |145146### Django/Python-Specific147148| Pattern | When | Trade-off |149|---|---|---|150| `select_related()` | ForeignKey lookups in loops | Larger initial query, but fewer total queries |151| `prefetch_related()` | Reverse FK / M2M lookups in loops | Extra query, but bounded number of queries |152| `.values()` / `.values_list()` | Only need specific columns | Lose model instance methods |153| Database indexes | Frequently filtered/ordered columns | Slower writes, disk space |154| Caching (`django.core.cache`) | Expensive queries repeated often | Stale data risk, cache invalidation complexity |155| Bulk operations | Creating/updating many rows | Less granular error handling |156157---158159## Trade-Off Analysis Template160161When recommending an optimization, always present:162163```markdown164### Optimization: [Description]165166**Current:** [What's happening now, with measured performance]167**Proposed:** [What to change]168169| Dimension | Before | After |170|---|---|---|171| Time complexity | O(n^2) | O(n log n) |172| Space complexity | O(1) | O(n) |173| Readability | Simple nested loop | Sort + scan requires comment |174| Maintainability | Easy to modify | Requires understanding of invariant |175176**Recommendation:** [Proceed / Defer / Skip]177**Justification:** [Why this trade-off is or isn't worth it in this context]178```179180---181182## Anti-Patterns: When NOT to Optimize183184- **Premature optimization.** If there's no measured performance problem, don't create complexity to solve one.185- **Micro-optimizing cold paths.** That function called once at startup? Leave it readable.186- **Optimizing without benchmarks.** "I think this is slow" is not evidence. Profile first.187- **Sacrificing correctness for speed.** Fast and wrong is worse than slow and right.188- **Cargo-culting.** "I read that HashMap is faster" — depends on the size, access pattern, and key type. Measure in your context.189190---191192## Integration193194- **Data:** Performance findings logged to `MEMORY.md`195- **Tools:** Can run profiling/benchmarking tools without asking (see execution policies in AGENTS.md)196- **Refactoring:** When optimization requires restructuring, use the refactoring-advisor skill for safe migration planning197- **Trade-offs:** Every optimization recommendation includes a trade-off table198199---200201_Arc skill — Performance profiling and optimization_