7.5 性能优化
性能分析方法
bash
# pprof 使用
# 1. HTTP 端点
import _ "net/http/pprof"
# 访问 http://localhost:6060/debug/pprof/
# 2. CPU 分析
go tool pprof http://localhost:6060/debug/pprof/profile
# 3. 内存分析
go tool pprof http://localhost:6060/debug/pprof/heap
# 4. 阻塞分析
go tool pprof http://localhost:6060/debug/pprof/block
# 5. 生成火焰图
go tool pprof -http=:8080 cpu.prof
# trace 工具
go test -trace=trace.out
go tool trace trace.outCPU 分析
go
package main
import (
"os"
"runtime/pprof"
)
func main() {
// CPU 分析
f, _ := os.Create("cpu.prof")
defer f.Close()
pprof.StartCPUProfile(f)
defer pprof.StopCPUProfile()
// 运行代码
for i := 0; i < 1000000; i++ {
process(i)
}
}
func process(n int) int {
result := 0
for j := 0; j < n; j++ {
result += j
}
return result
}
// 分析:
// go tool pprof cpu.prof
// (pprof) top10
// (pprof) list process内存分析
go
package main
import (
"os"
"runtime"
"runtime/pprof"
)
func main() {
// 运行代码产生内存分配
// 写入内存快照
f, _ := os.Create("mem.prof")
defer f.Close()
runtime.GC()
pprof.WriteHeapProfile(f)
}
// 常见内存问题:
// 1. 内存泄漏 (goroutine 泄漏)
func leak() {
ch := make(chan int)
go func() {
<-ch // 永远阻塞
}()
}
// 2. 不必要的分配
func badConcat() string {
s := ""
for i := 0; i < 1000; i++ {
s += "x" // 每次都分配
}
return s
}性能优化案例
go
// 1. 字符串拼接优化
// 优化前:100ms
func badConcat(parts []string) string {
result := ""
for _, p := range parts {
result += p
}
return result
}
// 优化后:1ms
func goodConcat(parts []string) string {
var sb strings.Builder
sb.Grow(len(parts) * 10)
for _, p := range parts {
sb.WriteString(p)
}
return sb.String()
}
// 2. 切片预分配
// 优化前
func badAppend(n int) []int {
s := make([]int, 0)
for i := 0; i < n; i++ {
s = append(s, i)
}
return s
}
// 优化后
func goodAppend(n int) []int {
s := make([]int, 0, n)
for i := 0; i < n; i++ {
s = append(s, i)
}
return s
}
// 3. Map 预分配
func goodMap() map[int]int {
m := make(map[int]int, 10000)
for i := 0; i < 10000; i++ {
m[i] = i
}
return m
}
// 4. 使用 sync.Pool
var pool = sync.Pool{
New: func() interface{} {
return make([]byte, 1024)
},
}
func process() {
buf := pool.Get().([]byte)
defer pool.Put(buf)
}
// 5. 避免接口装箱
func ProcessInt(v int) { } // 好
func ProcessAny(v interface{}) {} // 不好性能检查清单
1. 预设切片/Map 容量
2. 使用 strings.Builder 拼接字符串
3. 使用 sync.Pool 复用对象
4. 避免不必要的接口转换
5. 使用值类型代替指针 (小对象)
6. 使用 RWMutex 代替 Mutex (读多写少)
7. 使用 atomic 代替 Mutex (简单计数)
8. 避免 goroutine 泄漏
9. 使用缓冲 channel 减少阻塞
10. 定期分析 pprof 定位瓶颈