|$ curl https://forge-ai.dev/api/markdown?path=docs/performance/profiling
$cat docs/performance-profiling.md
updated Recently·30 min read·published
Performance Profiling
Introduction
Profiling identifies where your application spends time and memory. Without profiling, optimization is guesswork. With profiling, you can see exactly which functions are slow, which allocations cause GC pressure, and which code paths block the main thread.
Chrome DevTools Performance Panel
performance-api.ts
TypeScript
| 1 | // Performance API — programmatic profiling |
| 2 | // Record a performance timeline |
| 3 | performance.mark("start-heavy-work"); |
| 4 | heavyComputation(); |
| 5 | performance.mark("end-heavy-work"); |
| 6 | performance.measure("heavy-work", "start-heavy-work", "end-heavy-work"); |
| 7 | |
| 8 | // Read the measurements |
| 9 | const entries = performance.getEntriesByName("heavy-work"); |
| 10 | console.log(`Duration: ${entries[0].duration}ms`); |
| 11 | |
| 12 | // Long Task API — detect tasks blocking the main thread |
| 13 | const observer = new PerformanceObserver((list) => { |
| 14 | for (const entry of list.getEntries()) { |
| 15 | if (entry.duration > 50) { // Tasks > 50ms are "long" |
| 16 | console.warn("Long task:", entry.duration, "ms", entry.attribution); |
| 17 | sendToAnalytics({ |
| 18 | type: "long_task", |
| 19 | duration: entry.duration, |
| 20 | startTime: entry.startTime, |
| 21 | }); |
| 22 | } |
| 23 | } |
| 24 | }); |
| 25 | observer.observe({ entryTypes: ["longtask"] }); |
| 26 | |
| 27 | // Web Vitals through Performance Observer |
| 28 | const lcpObserver = new PerformanceObserver((list) => { |
| 29 | const entries = list.getEntries(); |
| 30 | const lastEntry = entries[entries.length - 1]; |
| 31 | console.log("LCP:", lastEntry.startTime, "element:", lastEntry.element); |
| 32 | }); |
| 33 | lcpObserver.observe({ type: "largest-contentful-paint", buffered: true }); |
devtools-workflow.sh
Bash
| 1 | # Chrome DevTools workflow: |
| 2 | # 1. Open DevTools → Performance tab |
| 3 | # 2. Click Record → interact with your app → Stop |
| 4 | # 3. Analyze the recording: |
| 5 | |
| 6 | # Key areas to look for: |
| 7 | # - Long tasks (red triangles in the timeline) |
| 8 | # - JavaScript execution (yellow bars) |
| 9 | # - Layout (purple bars) |
| 10 | # - Paint (green bars) |
| 11 | # - Recalculate Style (red blocks) |
| 12 | |
| 13 | # Flame chart navigation: |
| 14 | # - Click a bar to see details in the bottom panel |
| 15 | # - WASD keys to zoom and pan |
| 16 | # - Search for function names in the search bar |
| 17 | # - "Summary" tab shows time by category |
| 18 | # - "Bottom-Up" tab shows most expensive functions |
ℹ
info
Use Chrome DevTools Performance Monitor(Ctrl+Shift+P → "Show Performance Monitor") for real-time FPS, memory, and CPU usage while interacting with your app.
Node.js Profiling
node-profiling.sh
Bash
| 1 | # Built-in V8 profiler |
| 2 | node --prof app.js |
| 3 | # Generates isolate-*.log |
| 4 | |
| 5 | # Process the log |
| 6 | node --prof-process isolate-*.log > processed.txt |
| 7 | |
| 8 | # Flame graph with clinic.js |
| 9 | npm install -g clinic |
| 10 | clinic doctor -- node app.js |
| 11 | # Opens interactive flame chart in browser |
| 12 | |
| 13 | # Clinic flame graph |
| 14 | clinic flame -- node app.js |
| 15 | |
| 16 | # CPU profile with Chrome DevTools |
| 17 | node --inspect app.js |
| 18 | # Open chrome://inspect in Chrome |
| 19 | # Go to Profiler tab → Start recording |
| 20 | |
| 21 | # Memory profiling |
| 22 | node --inspect --expose-gc app.js |
| 23 | # In Chrome DevTools → Memory tab |
| 24 | # Take heap snapshots to find memory leaks |
React Profiling
react-profiling.tsx
TypeScript
| 1 | // React DevTools Profiler |
| 2 | // 1. Install React Developer Tools browser extension |
| 3 | // 2. Open DevTools → Profiler tab |
| 4 | // 3. Click Record → interact → Stop |
| 5 | |
| 6 | // Programmatic profiling |
| 7 | import { Profiler } from "react"; |
| 8 | |
| 9 | function onRenderCallback( |
| 10 | id: string, |
| 11 | phase: "mount" | "update" | "nested-update", |
| 12 | actualDuration: number, |
| 13 | baseDuration: number, |
| 14 | startTime: number, |
| 15 | commitTime: number, |
| 16 | ) { |
| 17 | if (actualDuration > 16) { // Flag slow renders (>1 frame) |
| 18 | console.warn(`Slow render: ${id} took ${actualDuration}ms`); |
| 19 | } |
| 20 | } |
| 21 | |
| 22 | function App() { |
| 23 | return ( |
| 24 | <Profiler id="App" onRender={onRenderCallback}> |
| 25 | <Dashboard /> |
| 26 | </Profiler> |
| 27 | ); |
| 28 | } |
| 29 | |
| 30 | // React DevTools advanced features: |
| 31 | // - Flamegraph chart: see component render times |
| 32 | // - Ranked chart: sort by render duration |
| 33 | // - Why did this render?: shows what caused re-render |
| 34 | // - Components → Profiler → "Record why each component rendered" |
✓
best practice
Use React.memo() and useMemo() only after profiling shows a component is re-rendering unnecessarily. Premature memoization adds complexity without measurable benefit.
Memory Profiling
memory-profiling.ts
TypeScript
| 1 | // Detect memory leaks with Performance API |
| 2 | if (typeof performance.memory !== "undefined") { |
| 3 | setInterval(() => { |
| 4 | const { usedJSHeapSize, totalJSHeapSize } = performance.memory; |
| 5 | console.log(`Memory: ${(usedJSHeapSize / 1e6).toFixed(1)}MB / ${(totalJSHeapSize / 1e6).toFixed(1)}MB`); |
| 6 | }, 5000); |
| 7 | } |
| 8 | |
| 9 | // Chrome DevTools Memory tab workflow: |
| 10 | // 1. Take a heap snapshot |
| 11 | // 2. Perform actions (navigate, interact) |
| 12 | // 3. Take another heap snapshot |
| 13 | // 4. Compare snapshots — objects that grew = potential leak |
| 14 | |
| 15 | // Common memory leak patterns: |
| 16 | // - Forgotten event listeners |
| 17 | // - Detached DOM nodes in closures |
| 18 | // - Growing arrays/maps without cleanup |
| 19 | // - Uncleared timers/intervals |
| 20 | // - WebSocket connections not closed |
| 21 | |
| 22 | // Fix: cleanup in useEffect |
| 23 | useEffect(() => { |
| 24 | const handler = () => {}; |
| 25 | window.addEventListener("resize", handler); |
| 26 | return () => window.removeEventListener("resize", handler); |
| 27 | }, []); |
$Blueprint — Engineering Documentation·Section ID: PERF-PRF-01·Revision: 1.0