ข้ามไปยังเนื้อหา

pprof Profiling

ใน Node.js คุณมี node --prof (V8 CPU profiler), node --inspect (Chrome DevTools) และเครื่องมืออย่าง clinic.js (clinic doctor, clinic flame) สำหรับวิเคราะห์ heap และ event loop ทั้งหมดทำงานแบบ out-of-process หรือไม่ก็ต้อง wrap คำสั่ง start

Go มาพร้อม runtime/pprof ใน standard library และ net/http/pprof สำหรับ profiling ผ่าน HTTP แบบ live — เก็บ profile ได้โดยไม่ต้องพึ่งเครื่องมือภายนอก และคำสั่ง go tool pprof ก็จัดการการวิเคราะห์ให้

หมายเหตุเรื่อง Playground: pprof ต้องเขียนไฟล์ profile ลง disk แล้วรัน go tool pprof บล็อกโค้ดด้านล่างคือไฟล์จริง — รันบนเครื่องคุณ

main.go
package main
import (
"os"
"runtime/pprof"
"log"
)
func main() {
// --- CPU profile ---
cpuFile, err := os.Create("cpu.out")
if err != nil {
log.Fatal(err)
}
defer cpuFile.Close()
if err := pprof.StartCPUProfile(cpuFile); err != nil {
log.Fatal(err)
}
defer pprof.StopCPUProfile()
// ... your code under test ...
doWork()
// --- Heap profile (write at the end) ---
heapFile, err := os.Create("mem.out")
if err != nil {
log.Fatal(err)
}
defer heapFile.Close()
pprof.WriteHeapProfile(heapFile)
}
main.go
package main
import (
"net/http"
_ "net/http/pprof" // blank import registers /debug/pprof/ routes
"log"
)
func main() {
// Your application server
go func() {
log.Println(http.ListenAndServe(":6060", nil))
}()
// ... rest of app ...
}

endpoint ที่มีให้:

  • http://localhost:6060/debug/pprof/ — index
  • http://localhost:6060/debug/pprof/profile?seconds=30 — CPU profile 30 วินาที
  • http://localhost:6060/debug/pprof/heap — heap snapshot
  • http://localhost:6060/debug/pprof/goroutine — stack ของทุก goroutine
  • http://localhost:6060/debug/pprof/trace?seconds=5 — execution trace
TypeScript
// Node.js — several tools, all external
// Option 1: built-in V8 profiler
node --prof server.js
node --prof-process isolate-*.log > report.txt
// Option 2: clinic.js (npm install -g clinic)
clinic doctor -- node server.js
clinic flame -- node server.js
// Option 3: Chrome DevTools
node --inspect server.js // open chrome://inspect
Go
// Go — single tool, ships with the toolchain
// Collect a 30-second CPU profile from a live server:
go tool pprof http://localhost:6060/debug/pprof/profile?seconds=30
// Collect from a file written by runtime/pprof:
go tool pprof cpu.out
// Open an interactive web UI (needs Graphviz):
go tool pprof -http=:8080 cpu.out
Terminal window
# Start interactive pprof shell
go tool pprof cpu.out
# Inside the shell:
(pprof) top10 # top 10 functions by CPU time
(pprof) list myFunc # annotated source for myFunc
(pprof) web # open SVG flame graph in browser
(pprof) pdf # save as PDF
# Or open the web UI directly (nicer flame graphs)
go tool pprof -http=:8080 cpu.out

ตัวอย่าง output จาก top10:

Showing nodes accounting for 2.40s, 96.77% of 2.48s total
flat flat% sum% cum cum%
1.20s 48.39% 48.39% 1.20s 48.39% runtime.mallocgc
0.60s 24.19% 72.58% 0.60s 24.19% strings.(*Builder).WriteString
0.30s 12.10% 84.68% 0.30s 12.10% main.processItem
คอลัมน์ความหมาย
flatเวลาที่ใช้ ใน ฟังก์ชันนี้ (ไม่รวม callees)
flat%เปอร์เซ็นต์ของ total profile time
cumเวลา cumulative (ฟังก์ชันนี้ + ฟังก์ชันทั้งหมดที่ฟังก์ชันนี้เรียกต่อ)
cum%เปอร์เซ็นต์ cumulative
Terminal window
go tool pprof mem.out
(pprof) top10 -cum # top allocators by cumulative bytes
(pprof) list myFunc # line-level allocation counts

heap profile แสดง:

  • alloc_objects / alloc_space — allocation รวมทั้งหมดตั้งแต่โปรแกรมเริ่มรัน
  • inuse_objects / inuse_space — object/byte ที่ยังถูกใช้อยู่ (หลัง GC)
Terminal window
# Write CPU + memory profiles during a benchmark run
go test -bench=BenchmarkFoo -cpuprofile=cpu.out -memprofile=mem.out ./...
# Then analyse
go tool pprof cpu.out
go tool pprof mem.out

ความเปลี่ยนแปลงใน runtime ที่กระทบสิ่งที่คุณเห็น (Go 1.25–1.27)

หัวข้อที่มีชื่อว่า “ความเปลี่ยนแปลงใน runtime ที่กระทบสิ่งที่คุณเห็น (Go 1.25–1.27)”
  • Green Tea GC (default ตั้งแต่ 1.26) ลด overhead ของ GC ลง 10–40 % ในโปรแกรมจริง ตัวเลข “ต้นทุน GC” ใน heap profile จากบทความเก่าจึงมองโลกในแง่ร้ายเกินไป ถ้าจะปิดชั่วคราวใช้ GOEXPERIMENT=nogreenteagc
  • GOMAXPROCS รู้จัก container (1.25): บน Linux runtime เคารพ cgroup CPU limit และอ่านค่าใหม่เป็นระยะ — คำแนะนำเก่าให้ import automaxprocs ทุก service ล้าสมัยแล้ว Node ไม่มีอะไรเทียบเท่า มันเห็นจำนวน CPU ของ host เสมอ
  • Goroutine leak profile (experimental ใน 1.26, GA ใน 1.27): GC หา goroutine ที่ block บน primitive ที่ไม่มีใครปลุกได้อีกแล้ว
Terminal window
# Go 1.27+: only goroutines that can never resume
go tool pprof http://localhost:6060/debug/pprof/goroutineleak

นี่คมกว่าการนั่งดูจำนวน /debug/pprof/goroutine ค่อย ๆ ไต่ขึ้น ซึ่งคือสิ่งที่ section ด้านล่างอธิบายมาก

Terminal window
# Check for goroutine leaks (should stay flat over time)
go tool pprof http://localhost:6060/debug/pprof/goroutine
# Execution trace — shows scheduling, GC, goroutine lifecycle
curl -s "http://localhost:6060/debug/pprof/trace?seconds=5" > trace.out
go tool trace trace.out

execution trace คือมุมมองที่ละเอียดที่สุด: แสดงทุกครั้งที่ goroutine start/stop, ทุก GC pause, ทุกครั้งที่ block รอ network และ system call — คล้ายกับที่ clinic doctor ทำให้ event loop ของ Node

blank import ใดที่ register HTTP endpoints /debug/pprof/?
ใน pprof output คอลัมน์ "flat" แสดงถึงอะไร?
คำสั่งใดที่เปิด interactive flame graph web UI สำหรับ profile file?
ทำไมถึงไม่ควร expose pprof endpoints บน public interface?