使用 pprof 排查性能问题
🔴 困难题目描述
以下代码存在性能问题,请使用 pprof 定位瓶颈并优化。
func ProcessItems(items []string) []string {
var results []string
for _, item := range items {
result := processItem(item)
results = append(results, result)
}
return results
}
func processItem(item string) string {
// 模拟复杂处理
var result string
for i := 0; i < 100; i++ {
result += fmt.Sprintf("%s-%d", item, i)
}
return result
}
func main() {
items := make([]string, 1000)
for i := range items {
items[i] = fmt.Sprintf("item-%d", i)
}
results := ProcessItems(items)
fmt.Println(len(results))
}提示
- 添加 pprof HTTP 服务
- 使用 go tool pprof 分析
- 查找 CPU 和内存热点
- 优化热点代码
解法
参考答案 (3 个标签)
pprof 性能优化 Benchmark
步骤 1:添加 pprof 服务
import (
_ "net/http/pprof" // 自动注册 pprof handler
"net/http"
)
func main() {
// 启动 pprof HTTP 服务
go func() {
log.Println(http.ListenAndServe("localhost:6060", nil))
}()
// 原有代码
items := make([]string, 1000)
for i := range items {
items[i] = fmt.Sprintf("item-%d", i)
}
results := ProcessItems(items)
fmt.Println(len(results))
}步骤 2:采集 CPU profile
# 采集 30 秒 CPU 数据
curl http://localhost:6060/debug/pprof/profile?seconds=30 > cpu.prof
# 或使用 go tool
go tool pprof http://localhost:6060/debug/pprof/profile?seconds=30步骤 3:分析 CPU profile
# 启动 pprof 交互界面
go tool pprof cpu.prof
# 查看热点函数
(pprof) top
# 输出示例:
# Showing nodes accounting for 1.5s, 50% of 3s total
# flat flat% sum% cum cum%
# 1.20s 40.00% 40.00% 1.50s 50.00% runtime.mallocgc
# 0.30s 10.00% 50.00% 0.80s 26.67% strings.Join
# 0.20s 6.67% 56.67% 0.50s 16.67% fmt.Sprintf
# 查看函数代码
(pprof) list processItem
# 生成火焰图
(pprof) web步骤 4:采集内存 profile
# 采集堆内存
curl http://localhost:6060/debug/pprof/heap > heap.prof
# 分析内存分配
go tool pprof heap.prof
# 查看内存分配热点
(pprof) top
# 输出示例:
# flat flat% sum% cum cum%
# 512MB 50.00% 50.00% 1024MB 100.00% processItem
# 256MB 25.00% 75.00% 512MB 50.00% ProcessItems步骤 5:优化代码
// ❌ 原代码:多次字符串拼接和格式化
func processItem(item string) string {
var result string
for i := 0; i < 100; i++ {
result += fmt.Sprintf("%s-%d", item, i) // 每次都创建新字符串
}
return result
}
// ✅ 优化 1:使用 strings.Builder
func processItemOptimized(item string) string {
var builder strings.Builder
builder.Grow(100 * (len(item) + 10)) // 预分配
for i := 0; i < 100; i++ {
builder.WriteString(item)
builder.WriteByte('-')
builder.WriteString(strconv.Itoa(i))
}
return builder.String()
}
// ✅ 优化 2:预分配切片
func ProcessItemsOptimized(items []string) []string {
results := make([]string, 0, len(items)) // 预分配
for _, item := range items {
result := processItemOptimized(item)
results = append(results, result)
}
return results
}步骤 6:验证优化效果
// 添加 Benchmark
func BenchmarkProcessItems(b *testing.B) {
items := make([]string, 1000)
for i := range items {
items[i] = fmt.Sprintf("item-%d", i)
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
ProcessItems(items)
}
}
func BenchmarkProcessItemsOptimized(b *testing.B) {
items := make([]string, 1000)
for i := range items {
items[i] = fmt.Sprintf("item-%d", i)
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
ProcessItemsOptimized(items)
}
}# 运行 Benchmark
go test -bench=. -benchmem
# 输出:
# BenchmarkProcessItems-8 100 10245678 ns/op 5120000 B/op 10000 allocs/op
# BenchmarkProcessItemsOptimized-8 10000 102456 ns/op 256000 B/op 1000 allocs/oppprof 常用命令
交互命令
# 进入交互界面
go tool pprof cpu.prof
# 查看热点
(pprof) top
(pprof) top10
(pprof) top -cum
# 查看函数代码
(pprof) list functionName
(pprof) list -lines functionName
# 查看调用图
(pprof) web
(pprof) web functionName
(pprof) pdf
(pprof) png
# 比较两个 profile
(pprof) base old.prof
(pprof) difference new.prof
# 保存报告
(pprof) png > output.png
(pprof) pdf > output.pdf命令行选项
# 采集 CPU profile(默认 30s)
go tool pprof http://localhost:6060/debug/pprof/profile?seconds=60
# 采集内存 profile
go tool pprof http://localhost:6060/debug/pprof/heap
# 采集分配对象
go tool pprof -alloc_space http://localhost:6060/debug/pprof/heap
# 采集使用内存
go tool pprof -inuse_space http://localhost:6060/debug/pprof/heap
# 采集 goroutine
go tool pprof http://localhost:6060/debug/pprof/goroutine
# 采集锁竞争
go tool pprof http://localhost:6060/debug/pprof/mutex
# 采集阻塞操作
go tool pprof http://localhost:6060/debug/pprof/blockpprof 在代码中集成
import (
"os"
"runtime/pprof"
)
func main() {
// CPU profiling
f, _ := os.Create("cpu.prof")
pprof.StartCPUProfile(f)
defer pprof.StopCPUProfile()
// 执行代码
// ...
// 内存 profiling
f2, _ := os.Create("mem.prof")
pprof.WriteHeapProfile(f2)
f2.Close()
}常见性能问题模式
1. 高 CPU 使用
// ❌ 低效算法
func FindDuplicate(nums []int) int {
for i := 0; i < len(nums); i++ {
for j := i + 1; j < len(nums); j++ {
if nums[i] == nums[j] {
return nums[i]
}
}
}
return -1
}
// ✅ 使用 Map
func FindDuplicateOptimized(nums []int) int {
seen := make(map[int]bool)
for _, num := range nums {
if seen[num] {
return num
}
seen[num] = true
}
return -1
}2. 高内存分配
// ❌ 频繁分配
func Concat(items []string) string {
var result string
for _, item := range items {
result += item // 每次都分配
}
return result
}
// ✅ 使用 Builder
func ConcatOptimized(items []string) string {
var builder strings.Builder
builder.Grow(estimateSize(items))
for _, item := range items {
builder.WriteString(item)
}
return builder.String()
}3. 锁竞争
// ❌ 粗粒度锁
type Counter struct {
mu sync.Mutex
count int
}
func (c *Counter) Increment() {
c.mu.Lock()
time.Sleep(time.Millisecond) // 持锁时间过长
c.count++
c.mu.Unlock()
}
// ✅ 减小临界区
func (c *Counter) IncrementOptimized() {
// 准备工作
time.Sleep(time.Millisecond)
// 只在必要时加锁
c.mu.Lock()
c.count++
c.mu.Unlock()
}