| name | perf-profile |
| description | Performance investigation: establish baseline, identify hot spots, form hypotheses, optimize, verify no regression. Includes instrumentation guidance. Use when investigating why something is slow.
|
| disable-model-invocation | true |
| argument-hint | [what is slow] |
Purpose
Guide systematic performance investigation rather than guesswork. Define
the performance question, establish a baseline, identify hot spots, form
hypotheses, implement targeted optimizations, and verify no regressions.
Also supports adding observability instrumentation to code.
Use sequential-bench to run benchmarks reliably. For broader
investigation including profiling and optimization, use this skill.
Instructions
0. Context recall (SeleneDB)
If SeleneDB is available (see selene-integration.md),
create a session and recall prior performance context:
-
Create session with skill: 'perf-profile' and scope: $ARGUMENTS
-
Scoped auto-recall — query for prior performance work:
- Prior
:Decision nodes from perf-profile sessions on same code
- Prior baselines and optimization outcomes
- Any
:Hypothesis nodes (from perf or debug) related to performance
-
If prior performance data exists:
"Prior performance context:
- [Prior baseline for this operation: date, metrics]
- [Optimizations previously applied: what worked, what didn't]
- [Any performance-related hypotheses from debug sessions]
This baseline history shows performance evolution over time."
Prior baselines that are worse than current indicate regression since the
last optimization. Prior baselines that are better indicate a new regression
to investigate.
If no prior context exists, skip silently.
1. Define the question
From $ARGUMENTS, clarify:
- What is slow? Specific operation, endpoint, or workload
- How slow? Current measurement (or "we don't know yet")
- Target? What performance is acceptable?
- Environment? Hardware, data size, concurrency level
If no baseline measurement exists, establish one first.
2. Establish baseline
Run the relevant benchmark or timing measurement. Record:
Baseline: [operation] at [data size] on [hardware]
Latency: p50=[X], p99=[Y]
Throughput: [N] ops/sec
Memory: [N] MB peak
Date: YYYY-MM-DD
Use the sequential-bench skill for Criterion benchmarks. For ad-hoc
measurements, use std::time::Instant (Rust), time.perf_counter()
(Python), or performance.now() (JavaScript).
3. Profile and identify hot spots
Rust profiling:
cargo flamegraph --bench <bench_name> -- --bench
cargo bench -p <crate> -- --profile-time 10
Python profiling:
python -m cProfile -o profile.out script.py
python -m py-spy record -o flame.svg -- python script.py
JavaScript profiling:
node --prof script.js
node --prof-process isolate-*.log > profile.txt
Look for:
- Functions consuming >10% of total time
- Unexpected allocation patterns (allocation in hot loops)
- I/O blocking async code
- Cache misses in data-heavy operations
4. Form hypotheses
For each hot spot, hypothesize why it is slow:
| Hot Spot | Hypothesis | Expected Impact |
|---|
expand_nodes() | HashMap lookup per node, O(n) | Batch lookup: 2-3x faster |
serialize() | Allocates String per field | Pre-allocated buffer: 30% faster |
Rank by expected impact. Start with the highest.
Graph write: baseline (SeleneDB)
After establishing the baseline, store it as an :Insight:
INSERT (i:Insight {
summary: $operation + ' baseline: ' + $metrics_summary,
sources: 'measured on ' + $hardware + ' at ' + $data_size,
confidence: 'high',
actionable: true
})
RETURN id(i) AS baseline_id
MATCH (s:Session) WHERE id(s) = $session_id
INSERT (s)-[:produced]->(i)
MERGE (loc:CodeLocation {file: $file, function: $function})
INSERT (i)-[:affects]->(loc)
Baselines stored in the graph enable cross-session comparison: "p50 was 12ms
last month, now it is 18ms — a 50% regression since commit X."
4b. Confirm test order with user
Present hypotheses to the user one at a time, starting with the highest
expected impact:
- For each hypothesis, present:
- The hot spot and hypothesis
- Expected impact if confirmed
- What the test involves (effort, risk)
- Ask: test this, skip, or reorder
- Wait for the user's decision before proceeding
"I have N hypotheses ranked by expected impact. Starting with the highest:
Hypothesis 1: [hot spot] - [hypothesis]
Expected impact: [estimate]
Test: [what you would do]
Test this one first, skip, or reorder?"
5. Optimize and measure
For each optimization:
- Implement the change
- Run the same benchmark as baseline
- Compare: did it improve? By how much?
- Check for regressions in related benchmarks
Optimization: [description]
Before: p50=[X], p99=[Y], [N] ops/sec
After: p50=[X'], p99=[Y'], [N'] ops/sec
Change: [+/-]N% latency, [+/-]N% throughput
If an optimization did not help, revert it. Do not keep speculative
optimizations.
Graph write: optimization result (SeleneDB)
After each optimization is measured:
INSERT (d:Decision {
summary: $optimization_description,
rationale: $before_after_metrics,
alternatives: $if_reverted_why,
confidence: $kept_or_reverted
})
RETURN id(d) AS opt_id
MATCH (s:Session) WHERE id(s) = $session_id
INSERT (s)-[:produced]->(d)
MERGE (loc:CodeLocation {file: $file, function: $function})
INSERT (d)-[:affects]->(loc)
Reverted optimizations are as valuable as kept ones — they prevent
re-trying approaches that were already measured and found ineffective.
6. Verify no regressions
After all optimizations:
- Run the full benchmark suite via
sequential-bench
- Compare all metrics against pre-optimization baselines
- Flag any regressions >10% in unrelated benchmarks
Instrumentation mode
When asked to add observability rather than investigate a specific issue:
Rust (tracing + OpenTelemetry):
use tracing::{info, instrument, warn};
#[instrument(skip(graph), fields(node_count = graph.len()))]
pub fn execute_query(graph: &Graph, query: &str) -> Result<Vec<Row>> {
info!("executing query");
}
Guidelines for instrumentation:
- Add
#[instrument] to public functions at API boundaries
- Skip large arguments with
skip(data), record summary fields
- Use
info! for business events, debug! for technical details
- Add timing spans around I/O operations and external calls
- Include context fields: request_id, user_id, operation name
- Do not log sensitive data (use the
safety-checks skill's rules)
Save performance investigation results to
_agentskills/reviews/YYYY-MM-DD-perf-<topic>.md.
Do not commit files in _agentskills/ unless the user explicitly asks.
Supporting files
Common Rationalizations
| Rationalization | Why It's Wrong |
|---|
| "I can see the slow code by inspection" | Intuition about performance is frequently wrong. Profile first, then optimize the measured hot spot. |
| "Multiple optimizations at once saves time" | Can't attribute improvement or regression. One change at a time is the only way to know what worked. |
| "No formal baseline, but it's obviously slow" | Without a number, you can't prove you improved anything. Measure before and after. |
| "Found hot spot, optimize without hypothesis" | Wrong hypothesis = wrong optimization = wasted effort. Predict before you change. |
| "Optimization works, skip regression check" | Performance gains that break correctness are not gains. Run the full suite. |
Red Flags
Stop and reassess if you observe:
- Optimizing without a measured baseline
- Applying multiple optimizations before measuring each independently
- Keeping an optimization that shows no measurable improvement
- Skipping the full regression check after optimization
Verification
Guidance
Measure before optimizing. Intuition about what is slow is frequently
wrong. Profile first, then optimize the measured hot spot.
One change at a time. If you make three optimizations simultaneously,
you cannot attribute the improvement (or regression) to any specific change.
Optimize the algorithm before the implementation. An O(n^2) algorithm
with optimized inner loop is still slower than an O(n log n) algorithm
with a naive inner loop at sufficient scale.
Know when to stop. If the target performance is met, stop. Further
optimization is speculative and may reduce readability.
SeleneDB tracks performance evolution. Baselines stored in the graph
show how performance changes over time. When a new perf-profile session
starts, the auto-recall shows prior baselines — making regressions
immediately visible and preventing redundant optimization attempts.