Performance Profiler
Interprets CPU profiles, heap dumps, flame graphs, and runtime metrics to identify the top performance bottlenecks and provide actionable, prioritized optimization recommendations.
When to Use
- User shares a flame graph,
perf report, pprof output, or profiler snapshot
- Application latency or CPU usage is higher than acceptable thresholds
- Memory usage grows over time (potential memory leak)
- User asks "why is this slow?" or "what's consuming the most CPU/memory?"
- Load testing reveals specific endpoints or functions as bottlenecks
- GC pressure or event loop lag is impacting throughput
Process
Identify the profiling format and runtime:
- Node.js: V8 CPU profile (JSON),
clinic flame, 0x flamegraph, heap snapshot
- Python:
cProfile/pstats output, py-spy SVG, memray reports
- Go:
pprof CPU/memory profile, go tool pprof text output
- JVM: JFR recording, VisualVM snapshot, async-profiler flamegraph
- Native: Linux
perf record/perf report, Instruments (macOS)
- Ruby:
stackprof output, rbspy
Parse the top consumers from the profile:
- For CPU profiles: identify functions with the highest self time (on-CPU, not waiting on callees) and highest total time (cumulative including callees)
- For heap profiles: identify allocation sites with the most bytes retained vs. allocated
- For flame graphs: find the widest frames (most time spent) — especially wide leaves
Categorize bottlenecks:
- CPU-bound: tight loops, expensive algorithms, redundant recomputation → optimize algorithm, add caching, parallelize
- I/O-bound: blocking disk/network calls in hot paths → make async, batch, prefetch
- Memory: large object allocations, object leaks, excessive GC → pool objects, fix leaks, reduce allocation rate
- Contention: lock contention, mutex hotspots → reduce lock scope, use lock-free structures
- GC pressure: high allocation rate causing frequent GC pauses → object pooling, reduce allocations
Rank findings by impact — estimate the % of total time or bytes each bottleneck represents.
Propose concrete optimizations for the top 3–5 bottlenecks:
- Show the current code (if available) and the optimized version
- Explain the mechanism of improvement (e.g., "reduces O(n²) to O(n log n)")
- Estimate the expected improvement if possible
Check for common runtime-specific anti-patterns:
- Node.js: synchronous fs calls,
JSON.parse on large payloads in event loop, unresolved promises accumulating
- Python: CPython GIL contention in CPU-bound multithreaded code, list comprehensions on large datasets vs. generators
- Go: goroutine leaks, channel operations in tight loops, interface boxing allocations
- JVM: autoboxing, string concatenation in loops, excessive object creation in hot paths
Recommend profiling tooling improvements if the current data is insufficient for diagnosis.
Output Format
## Performance Analysis Report
**Profile type:** CPU Flame Graph (Node.js, 30s capture under load)
**Total samples:** 15,420
---
### Top Bottlenecks
| Rank | Function | Self % | Total % | Category |
|------|----------|--------|---------|----------|
| 1 | `processPayload` (src/worker.js:44) | 34% | 58% | CPU-bound |
| 2 | `JSON.parse` (built-in) | 22% | 22% | CPU-bound |
| 3 | `getFromDatabase` (src/db.js:112) | 18% | 18% | I/O wait |
---
### Finding 1: Redundant JSON Parsing (34% → potentially 10%)
`processPayload` calls `JSON.parse(JSON.stringify(obj))` to deep-clone objects.
This is extremely expensive at high throughput.
**Fix:** Use a structured clone or a purpose-built deep-clone library:
```js
// ❌ Current — expensive serialization round-trip
const copy = JSON.parse(JSON.stringify(data));
// ✅ Fix — use structuredClone (Node 17+) or lodash cloneDeep
const copy = structuredClone(data);
Estimated saving: ~24% CPU reduction based on profile weight.
Finding 2: Synchronous Database Calls Blocking Event Loop
getFromDatabase uses the synchronous sqlite3 API, blocking the event loop
for up to 18ms per call under load.
Fix: Migrate to the async API or use better-sqlite3 with worker threads
to keep the event loop free.
## Examples
### Example Input
Python cProfile output (sorted by cumulative time):
ncalls tottime cumtime filename:lineno(function)
100000 8.234 21.456 process.py:34(transform_record)
100000 0.123 13.222 validate.py:12(validate_schema)
1000000 12.100 12.100 {method 'match' of 're.Pattern'}
### Example Output
Analysis
Top bottleneck: regex compilation in hot path (12.1s / 56% of total)
validate_schema (called 100,000 times) recompiles regex patterns on every call.
re.match(pattern_string, ...) recompiles the pattern each invocation.
Fix: Compile patterns once at module load:
# ❌ Current — recompiles on every call
def validate_schema(value):
return re.match(r'^[a-z0-9_]+$', value)
# ✅ Fix — compile once
_PATTERN = re.compile(r'^[a-z0-9_]+$')
def validate_schema(value):
return _PATTERN.match(value)
Estimated saving: 10–12 seconds (eliminating repeated compilation overhead).
## Boundaries
- Do NOT recommend premature optimization — only optimize code that shows up in the actual profile.
- Do NOT suggest algorithmic rewrites without reviewing the actual function code.
- If profile data is ambiguous or incomplete, state what additional data would be needed for a confident diagnosis.
- Do NOT recommend disabling GC, using unsafe memory access, or other dangerous low-level optimizations without strong justification and caveats.
- Always measure before and after optimization — recommend adding benchmarks if they don't exist.
- Do NOT assume the bottleneck is always in application code — it may be in the database, network, or infrastructure.
1---2name: performance-profiler3description: Interprets profiling output (flame graphs, heap dumps, perf reports) and highlights the top bottlenecks with optimization advice. Invoke when asked to analyze performance data, interpret a flame graph, diagnose slow code, reduce CPU or memory usage, or find hot paths.4---56# Performance Profiler78Interprets CPU profiles, heap dumps, flame graphs, and runtime metrics to identify the top performance bottlenecks and provide actionable, prioritized optimization recommendations.910## When to Use1112- User shares a flame graph, `perf` report, pprof output, or profiler snapshot13- Application latency or CPU usage is higher than acceptable thresholds14- Memory usage grows over time (potential memory leak)15- User asks "why is this slow?" or "what's consuming the most CPU/memory?"16- Load testing reveals specific endpoints or functions as bottlenecks17- GC pressure or event loop lag is impacting throughput1819## Process20211. **Identify the profiling format and runtime**:22 - Node.js: V8 CPU profile (JSON), `clinic flame`, `0x` flamegraph, heap snapshot23 - Python: `cProfile`/`pstats` output, `py-spy` SVG, `memray` reports24 - Go: `pprof` CPU/memory profile, `go tool pprof` text output25 - JVM: JFR recording, VisualVM snapshot, async-profiler flamegraph26 - Native: Linux `perf record`/`perf report`, Instruments (macOS)27 - Ruby: `stackprof` output, `rbspy`28292. **Parse the top consumers** from the profile:30 - For CPU profiles: identify functions with the highest **self time** (on-CPU, not waiting on callees) and highest **total time** (cumulative including callees)31 - For heap profiles: identify allocation sites with the most bytes retained vs. allocated32 - For flame graphs: find the widest frames (most time spent) — especially wide leaves33343. **Categorize bottlenecks**:35 - **CPU-bound**: tight loops, expensive algorithms, redundant recomputation → optimize algorithm, add caching, parallelize36 - **I/O-bound**: blocking disk/network calls in hot paths → make async, batch, prefetch37 - **Memory**: large object allocations, object leaks, excessive GC → pool objects, fix leaks, reduce allocation rate38 - **Contention**: lock contention, mutex hotspots → reduce lock scope, use lock-free structures39 - **GC pressure**: high allocation rate causing frequent GC pauses → object pooling, reduce allocations40414. **Rank findings by impact** — estimate the % of total time or bytes each bottleneck represents.42435. **Propose concrete optimizations** for the top 3–5 bottlenecks:44 - Show the current code (if available) and the optimized version45 - Explain the mechanism of improvement (e.g., "reduces O(n²) to O(n log n)")46 - Estimate the expected improvement if possible47486. **Check for common runtime-specific anti-patterns**:49 - Node.js: synchronous fs calls, `JSON.parse` on large payloads in event loop, unresolved promises accumulating50 - Python: CPython GIL contention in CPU-bound multithreaded code, list comprehensions on large datasets vs. generators51 - Go: goroutine leaks, channel operations in tight loops, interface boxing allocations52 - JVM: autoboxing, string concatenation in loops, excessive object creation in hot paths53547. **Recommend profiling tooling improvements** if the current data is insufficient for diagnosis.5556## Output Format5758```59## Performance Analysis Report6061**Profile type:** CPU Flame Graph (Node.js, 30s capture under load)62**Total samples:** 15,4206364---6566### Top Bottlenecks6768| Rank | Function | Self % | Total % | Category |69|------|----------|--------|---------|----------|70| 1 | `processPayload` (src/worker.js:44) | 34% | 58% | CPU-bound |71| 2 | `JSON.parse` (built-in) | 22% | 22% | CPU-bound |72| 3 | `getFromDatabase` (src/db.js:112) | 18% | 18% | I/O wait |7374---7576### Finding 1: Redundant JSON Parsing (34% → potentially 10%)77`processPayload` calls `JSON.parse(JSON.stringify(obj))` to deep-clone objects.78This is extremely expensive at high throughput.7980**Fix:** Use a structured clone or a purpose-built deep-clone library:81```js82// ❌ Current — expensive serialization round-trip83const copy = JSON.parse(JSON.stringify(data));8485// ✅ Fix — use structuredClone (Node 17+) or lodash cloneDeep86const copy = structuredClone(data);87```88**Estimated saving:** ~24% CPU reduction based on profile weight.8990---9192### Finding 2: Synchronous Database Calls Blocking Event Loop93`getFromDatabase` uses the synchronous sqlite3 API, blocking the event loop94for up to 18ms per call under load.9596**Fix:** Migrate to the async API or use `better-sqlite3` with worker threads97to keep the event loop free.98```99100## Examples101102### Example Input103```104Python cProfile output (sorted by cumulative time):105 ncalls tottime cumtime filename:lineno(function)106 100000 8.234 21.456 process.py:34(transform_record)107 100000 0.123 13.222 validate.py:12(validate_schema)108 1000000 12.100 12.100 {method 'match' of 're.Pattern'}109```110111### Example Output112```113## Analysis114115**Top bottleneck: regex compilation in hot path (12.1s / 56% of total)**116117`validate_schema` (called 100,000 times) recompiles regex patterns on every call.118`re.match(pattern_string, ...)` recompiles the pattern each invocation.119120**Fix:** Compile patterns once at module load:121```python122# ❌ Current — recompiles on every call123def validate_schema(value):124 return re.match(r'^[a-z0-9_]+$', value)125126# ✅ Fix — compile once127_PATTERN = re.compile(r'^[a-z0-9_]+$')128129def validate_schema(value):130 return _PATTERN.match(value)131```132**Estimated saving:** 10–12 seconds (eliminating repeated compilation overhead).133```134135## Boundaries136137- Do NOT recommend premature optimization — only optimize code that shows up in the actual profile.138- Do NOT suggest algorithmic rewrites without reviewing the actual function code.139- If profile data is ambiguous or incomplete, state what additional data would be needed for a confident diagnosis.140- Do NOT recommend disabling GC, using unsafe memory access, or other dangerous low-level optimizations without strong justification and caveats.141- Always measure before and after optimization — recommend adding benchmarks if they don't exist.142- Do NOT assume the bottleneck is always in application code — it may be in the database, network, or infrastructure.