| name | rtk-performance |
| version | 1.0.0 |
| targets | ["claude-code"] |
| type | skill |
| description | Use when investigating, benchmarking, or fixing rtk performance regressions — enforces <10ms startup, <5MB resident memory, 60-90% token savings, <5MB stripped binary; covers lazy_static regex, zero-copy parsing, dependency minimisation, and flamegraph workflow |
| category | {"primary":"workflow"} |
| license | {"upstream":"Apache-2.0","source":"rtk-ai/rtk@3ba1634","path":".claude/skills/performance/SKILL.md"} |
Performance Optimization Skill
Systematic performance analysis and optimization for RTK CLI tool, focusing on startup time (<10ms), memory usage (<5MB), and token savings (60-90%).
When to Use
- Automatically triggered: After filter changes, regex modifications, or dependency additions
- Manual invocation: When performance degradation suspected or before release
- Proactive: After any code change that could impact startup time or memory
RTK Performance Targets
| Metric | Target | Verification Method | Failure Threshold |
|---|
| Startup time | <10ms | hyperfine 'rtk <cmd>' | >15ms = blocker |
| Memory usage | <5MB resident | /usr/bin/time -l rtk <cmd> (macOS) | >7MB = blocker |
| Token savings | 60-90% | Tests with count_tokens() | <60% = blocker |
| Binary size | <5MB stripped | ls -lh target/release/rtk | >8MB = investigate |
Performance Analysis Workflow
1. Establish Baseline
Before making any changes, capture current performance:
hyperfine 'rtk git status' --warmup 3 --export-json /tmp/baseline_startup.json
/usr/bin/time -l rtk git status 2>&1 | grep "maximum resident set size" > /tmp/baseline_memory.txt
/usr/bin/time -v rtk git status 2>&1 | grep "Maximum resident set size" > /tmp/baseline_memory.txt
ls -lh target/release/rtk | tee /tmp/baseline_binary_size.txt
2. Make Changes
Implement optimization or feature changes.
3. Rebuild and Measure
cargo build --release
hyperfine 'target/release/rtk git status' --warmup 3 --export-json /tmp/after_startup.json
/usr/bin/time -l target/release/rtk git status 2>&1 | grep "maximum resident set size" > /tmp/after_memory.txt
ls -lh target/release/rtk | tee /tmp/after_binary_size.txt
4. Compare Results
hyperfine 'rtk git status' 'target/release/rtk git status' --warmup 3
diff /tmp/baseline_memory.txt /tmp/after_memory.txt
diff /tmp/baseline_binary_size.txt /tmp/after_binary_size.txt
5. Identify Regressions
Startup time regression (>15% increase or >2ms absolute):
cargo install flamegraph
cargo flamegraph -- target/release/rtk git status
open flamegraph.svg
Memory regression (>20% increase or >1MB absolute):
cargo +nightly build --release -Z build-std
RUSTFLAGS="-C link-arg=-fuse-ld=lld" cargo +nightly build --release
cargo install dhat
Token savings regression (<60% savings):
cargo test test_token_savings
Common Performance Issues
Issue 1: Regex Recompilation
Symptom: Startup time >20ms, flamegraph shows regex compilation in hot path
Detection:
cargo flamegraph -- target/release/rtk git log -10
Fix:
fn filter_line(line: &str) -> Option<&str> {
let re = Regex::new(r"pattern").unwrap();
re.find(line).map(|m| m.as_str())
}
use lazy_static::lazy_static;
lazy_static! {
static ref LINE_PATTERN: Regex = Regex::new(r"pattern").unwrap();
}
fn filter_line(line: &str) -> Option<&str> {
LINE_PATTERN.find(line).map(|m| m.as_str())
}
Issue 2: Excessive Allocations
Symptom: Memory usage >5MB, many small allocations in flamegraph
Detection:
cargo +nightly build --release
valgrind --tool=dhat target/release/rtk git status
Fix:
fn filter_lines(input: &str) -> String {
input.lines()
.map(|line| line.to_string())
.collect::<Vec<_>>()
.join("\n")
}
fn filter_lines(input: &str) -> String {
input.lines()
.collect::<Vec<_>>()
.join("\n")
}
Issue 3: Startup I/O
Symptom: Startup time varies wildly (5ms to 50ms), flamegraph shows file reads
Detection:
strace -c target/release/rtk git status 2>&1 | grep -E "open|read"
sudo dtrace -n 'syscall::open*:entry { @[execname] = count(); }' &
target/release/rtk git status
sudo pkill dtrace
Fix:
fn main() {
let config = load_config().unwrap();
}
fn main() {
}
Issue 4: Dependency Bloat
Symptom: Binary size >5MB, many unused dependencies in Cargo.toml
Detection:
cargo tree
cargo install cargo-bloat
cargo bloat --release --crates
Fix:
[dependencies]
clap = { version = "4", features = ["derive", "color", "suggestions"] }
[dependencies]
clap = { version = "4", features = ["derive"], default-features = false }
Optimization Techniques
Technique 1: Lazy Static Initialization
Use case: Regex patterns, static configuration, one-time allocations
Implementation:
use lazy_static::lazy_static;
use regex::Regex;
lazy_static! {
static ref COMMIT_HASH: Regex = Regex::new(r"[0-9a-f]{7,40}").unwrap();
static ref AUTHOR_LINE: Regex = Regex::new(r"^Author: (.+)$").unwrap();
static ref DATE_LINE: Regex = Regex::new(r"^Date: (.+)$").unwrap();
}
Impact: ~5-10ms saved per regex pattern (if compiled at runtime)
Technique 2: Zero-Copy String Processing
Use case: Filter output without allocating intermediate Strings
Implementation:
fn filter(input: &str) -> String {
input.lines()
.filter(|line| !line.is_empty())
.map(|line| line.to_string())
.collect::<Vec<_>>()
.join("\n")
}
fn filter(input: &str) -> String {
input.lines()
.filter(|line| !line.is_empty())
.collect::<Vec<_>>()
.join("\n")
}
Impact: ~1-2MB memory saved, ~1-2ms startup saved
Technique 3: Minimal Dependencies
Use case: Reduce binary size and compile time
Implementation:
[dependencies]
clap = { version = "4", features = ["derive"], default-features = false }
serde = { version = "1", features = ["derive"], default-features = false }
Impact: ~1-2MB binary size reduction, ~2-5ms startup saved
Performance Testing Checklist
Before committing filter changes:
Startup Time
Memory Usage
Token Savings
Binary Size
Continuous Performance Monitoring
Pre-Commit Hook
Add to .claude/hooks/bash/pre-commit-performance.sh:
#!/bin/bash
echo "Running performance checks..."
CURRENT_TIME=$(hyperfine 'rtk git status' --warmup 3 --export-json /tmp/perf.json 2>&1 | grep "Time (mean" | awk '{print $4}')
CURRENT_MS=$(echo $CURRENT_TIME | sed 's/ms//')
if (( $(echo "$CURRENT_MS > 10" | bc -l) )); then
echo "Startup time regression: ${CURRENT_MS}ms (target: <10ms)"
exit 1
fi
BINARY_SIZE=$(ls -l target/release/rtk | awk '{print $5}')
MAX_SIZE=$((5 * 1024 * 1024))
if [ $BINARY_SIZE -gt $MAX_SIZE ]; then
echo "Binary size regression: $(($BINARY_SIZE / 1024 / 1024))MB (target: <5MB)"
exit 1
fi
echo "Performance checks passed"
CI/CD Integration
Add to .github/workflows/ci.yml:
- name: Performance Regression Check
run: |
cargo build --release
cargo install hyperfine
hyperfine 'target/release/rtk git status' --warmup 3 --max-runs 10
BINARY_SIZE=$(ls -l target/release/rtk | awk '{print $5}')
MAX_SIZE=$((5 * 1024 * 1024))
if [ $BINARY_SIZE -gt $MAX_SIZE ]; then
echo "Binary too large: $(($BINARY_SIZE / 1024 / 1024))MB"
exit 1
fi
Performance Optimization Priorities
Priority order (highest to lowest impact):
- Lazy static regex (5-10ms per pattern if compiled at runtime)
- Remove startup I/O (10-50ms for config file reads)
- Zero-copy processing (1-2MB memory, 1-2ms startup)
- Minimal dependencies (1-2MB binary, 2-5ms startup)
- Algorithm optimization (varies, measure first)
When in doubt: Profile first with flamegraph, then optimize the hottest path.
Tools Reference
| Tool | Purpose | Command |
|---|
| hyperfine | Benchmark startup time | hyperfine 'rtk <cmd>' --warmup 3 |
| time | Memory usage (macOS) | /usr/bin/time -l rtk <cmd> |
| time | Memory usage (Linux) | /usr/bin/time -v rtk <cmd> |
| flamegraph | CPU profiling | cargo flamegraph -- rtk <cmd> |
| cargo bloat | Binary size analysis | cargo bloat --release --crates |
| cargo tree | Dependency tree | cargo tree |
| DHAT | Heap profiling | cargo +nightly build && valgrind --tool=dhat |
| strace | System call tracing (Linux) | strace -c target/release/rtk <cmd> |
| dtrace | System call tracing (macOS) | sudo dtrace -n 'syscall::open*:entry' |
Install tools:
brew install hyperfine
cargo install hyperfine
cargo install flamegraph
cargo install cargo-bloat