- Backend profiling with pprof: profile types, flame graph reading, common hotspots - Frontend profiling with Chrome DevTools: Performance panel, Memory panel - Diagnostic workflows for scrolling jank, slow navigation, memory growth - References scripts/profile.sh for quick access to all profile types
7.2 KiB
7.2 KiB
Performance Profiling Guide
Practical guide for diagnosing performance issues in YellowJacket. This covers backend (Go/pprof), frontend (Chrome DevTools), and specific diagnostic workflows.
1. Backend Profiling (Go / pprof)
Setup
make dev starts the app with a pprof server on :6060. No extra configuration needed — block and mutex profiling are enabled automatically in dev builds.
Quick Start
./scripts/profile.sh # Interactive menu
./scripts/profile.sh cpu # 30s CPU profile (flame graph in browser)
./scripts/profile.sh heap # Current memory usage
./scripts/profile.sh health # Goroutine count, heap, GC stats
Profile Types
| Profile | Use When | What It Shows |
|---|---|---|
| CPU | Something is slow | Time spent in each function (flame graph) |
| Heap | Memory growing | Current allocations by location |
| Allocs | GC pressure | Where allocations happen (even freed) |
| Goroutine | Hangs/deadlocks | All goroutines and their stack traces |
| Block | Lock contention | Where goroutines block on mutexes/channels |
| Mutex | Mutex bottleneck | Mutex contention hotspots |
| Trace | Scheduling issues | Timeline of goroutine scheduling, GC pauses, syscalls |
Reading Flame Graphs
- Wide bars = more time spent in that function
- Look for unexpectedly wide bars (functions taking more time than they should)
- Bottom of the stack = entry points; top = leaf functions where time is actually spent
- Use the search box to filter by package (e.g.
library,queue,database) - Click a bar to zoom into that subtree; click "Root" to zoom back out
Common YellowJacket Hotspots
| Function | What to Check |
|---|---|
database.GetAllTracks |
Large library — check SQL query time, consider pagination |
library.extractMetadata |
Scan performance — check per-format timing in scan metrics |
queue.SetQueue |
Large queues — Phase 1/2 dedup and index rebuild |
coverart.Generate* |
Thumbnail generation — check per-tier timing |
database.SearchTracks |
FTS5 query performance — check query complexity |
Manual pprof Access
If you prefer direct access without the script:
# CPU profile (30 seconds, opens flame graph)
go tool pprof -http=:8080 http://localhost:6060/debug/pprof/profile?seconds=30
# Heap profile
go tool pprof -http=:8080 http://localhost:6060/debug/pprof/heap
# Goroutine dump (text)
curl http://localhost:6060/debug/pprof/goroutine?debug=1
# Execution trace (5 seconds)
curl -o trace.out http://localhost:6060/debug/trace?seconds=5
go tool trace trace.out
2. Frontend Profiling (Chrome DevTools)
YellowJacket uses Wails (WebView2 on Linux/Windows, WebKit on macOS). On dev builds, Chrome DevTools is available for frontend profiling.
Opening DevTools
Press Ctrl+Shift+I in a Wails dev build (or right-click → Inspect).
Performance Panel (Scrolling & Rendering)
- Open the Performance panel
- Click Record (circle icon)
- Perform the action you want to profile (scroll, navigate, etc.)
- Click Stop
- Analyze the timeline:
| Bar Color | Meaning | Target |
|---|---|---|
| Yellow | JavaScript execution | < 5ms per frame |
| Purple | Rendering / layout | Minimal during scroll |
| Green | Painting | Thin bars = composited (good) |
| Grey | Idle | Expected between frames |
Target: each frame should complete in < 16ms for 60fps scrolling.
Key Metrics for Scroll Smoothness
- Frame time: Should be consistently < 16ms. Check the FPS row at the top.
- Layout recalculation: Should NOT happen during scrolling. If it does, CSS
containisn't working or a style is being read/written in a scroll handler. - Paint area: Should be minimal. Large paint rects during scroll indicate missing
will-change: transformon the scroll container. - JS execution during scroll: Should be minimal. lit-virtualizer handles virtualization, but
renderItemcallbacks run for each new item entering the viewport.
Memory Panel
- Take a heap snapshot before an action
- Perform the action (navigate views, scroll extensively)
- Take another heap snapshot
- Switch to Comparison view between the two snapshots
- Look for growing arrays or detached DOM nodes
What to Look For
| Symptom | Likely Cause | How to Check |
|---|---|---|
| Scroll jank | Layout thrashing | Performance panel → look for "Layout" bars during scroll |
| Slow navigation | View recreation | Performance panel → long constructors after navigate event |
| Memory growth | Listener leaks | Memory panel → compare snapshots, filter "Detached" |
| Slow initial load | Blocking JS | Performance panel → gap between DOMContentLoaded and first paint |
| Flickering on navigate | View not cached | Check viewCache in app-layout — should be cached for primary views |
3. Profiling Workflow for Specific Issues
"Scrolling feels janky"
- Open DevTools → Performance panel
- Click Record → scroll the problematic view for 3-5 seconds → Stop
- Look at frame times — are any > 16ms?
- If JS is the bottleneck: Check
renderItemcallback time. Are closures being created per-render? (Phase 14-03 eliminated this pattern) - If Layout is the bottleneck: Check if CSS
containis present on scroll containers. The app usescontain: layout styleon the main panel andcontain: painton virtualizers. - If Paint is the bottleneck: Check if
will-change: transformis on the scroll container. All scroll-heavy components should have this after Phase 14-01.
"Navigation is slow"
- Open DevTools → Performance panel
- Click Record → navigate between views rapidly → Stop
- Look for long JS tasks between the navigate event and first paint
- Check if the view is being destroyed/recreated (look for constructor calls)
- After Phase 14-02 view caching: navigation between primary views (Tracks, Albums, Artists, Genres, Playlists, Settings) should show almost no JS activity — the cached DOM is simply shown/hidden
"Library operations feel slow"
- Run
./scripts/profile.sh cpu— capture for the duration of the operation - Check the flame graph for the specific Go function
- For database operations: check if SQL queries are using indexes
- For scan operations: scan metrics are already logged — check per-file timing
- Run
./scripts/profile.sh tracefor detailed goroutine scheduling during the operation
"Memory keeps growing"
- Run
./scripts/profile.sh heapat baseline - Perform the suspect operation repeatedly
- Run
./scripts/profile.sh heapagain — compare the flame graphs - For frontend: use DevTools Memory panel heap snapshot comparison
- Common causes: event listeners not cleaned up in
disconnectedCallback, store subscriptions not unsubscribed, large arrays held by closed-over references
"App feels sluggish after running for a while"
./scripts/profile.sh health— check goroutine count (should be stable, not growing)./scripts/profile.sh heap— check if heap is much larger than expected- Check GC stats: high GC cycle count with growing heap = possible memory leak
- Frontend: check for detached DOM nodes in DevTools Memory panel