Skip to content
清晨的一缕阳光
返回

Go 性能优化实战

Go 性能优化实战

Go 性能优化需要系统的方法论和合适的工具,理解原理才能有效调优。

一、性能分析工具

1.1 pprof

# CPU 分析
go test -cpuprofile=cpu.prof ./...
go tool pprof cpu.prof

# 内存分析
go test -memprofile=mem.prof ./...
go tool pprof mem.prof

# Web 界面
go tool pprof -http=:8080 cpu.prof

1.2 trace

# 生成 trace 文件
go test -trace=trace.out ./...

# 查看 trace
go tool trace trace.out

1.3 运行时统计

import "runtime"

var m runtime.MemStats
runtime.ReadMemStats(&m)

fmt.Printf("Alloc = %v KB", m.Alloc/1024)
fmt.Printf("TotalAlloc = %v KB", m.TotalAlloc/1024)
fmt.Printf("Sys = %v KB", m.Sys/1024)
fmt.Printf("NumGC = %v", m.NumGC)

二、内存优化

2.1 减少分配

// ❌ 避免:频繁分配
for i := 0; i < 1000000; i++ {
    s := make([]byte, 1024)
    _ = s
}

// ✅ 推荐:复用内存
buf := make([]byte, 1024)
for i := 0; i < 1000000; i++ {
    _ = buf[:1024]
}

2.2 sync.Pool

var bufferPool = sync.Pool{
    New: func() any {
        return make([]byte, 4096)
    },
}

func getBuffer() []byte {
    return bufferPool.Get().([]byte)
}

func putBuffer(buf []byte) {
    bufferPool.Put(buf[:0])
}

2.3 预分配容量

// ❌ 避免:多次扩容
var slice []int
for i := 0; i < 1000000; i++ {
    slice = append(slice, i)
}

// ✅ 推荐:预分配
slice := make([]int, 0, 1000000)
for i := 0; i < 1000000; i++ {
    slice = append(slice, i)
}

2.4 字符串优化

// ❌ 避免:频繁拼接
s := ""
for i := 0; i < 10000; i++ {
    s += strconv.Itoa(i)
}

// ✅ 推荐:strings.Builder
var builder strings.Builder
builder.Grow(10000 * 10) // 预分配
for i := 0; i < 10000; i++ {
    builder.WriteString(strconv.Itoa(i))
}
s := builder.String()

三、并发优化

3.1 控制并发数

// ❌ 避免:无限制并发
for i := 0; i < 1000000; i++ {
    go func() {
        // ...
    }()
}

// ✅ 推荐:限制并发数
sem := make(chan struct{}, 100)
for i := 0; i < 1000000; i++ {
    sem <- struct{}{}
    go func() {
        defer func() { <-sem }()
        // ...
    }()
}

3.2 工作池模式

type WorkerPool struct {
    jobs    chan func()
    workers int
}

func NewWorkerPool(workers int) *WorkerPool {
    wp := &WorkerPool{
        jobs:    make(chan func(), 1000),
        workers: workers,
    }
    
    for i := 0; i < workers; i++ {
        go wp.worker()
    }
    
    return wp
}

func (wp *WorkerPool) worker() {
    for job := range wp.jobs {
        job()
    }
}

func (wp *WorkerPool) Submit(job func()) {
    wp.jobs <- job
}

3.3 避免 Goroutine 泄漏

// ❌ 避免:无退出条件
func worker(ch chan int) {
    for v := range ch {
        process(v)
    }
}

// ✅ 推荐:使用 context
func worker(ctx context.Context, ch chan int) {
    for {
        select {
        case <-ctx.Done():
            return
        case v := <-ch:
            process(v)
        }
    }
}

四、I/O 优化

4.1 缓冲 I/O

// ❌ 避免:无缓冲
file, _ := os.Open("file.txt")
scanner := bufio.NewScanner(file)
for scanner.Scan() {
    // ...
}

// ✅ 推荐:缓冲
file, _ := os.Open("file.txt")
reader := bufio.NewReaderSize(file, 32*1024)
for {
    line, err := reader.ReadBytes('\n')
    if err != nil {
        break
    }
    // ...
}

4.2 批量操作

// ❌ 避免:逐条写入
for _, item := range items {
    db.Exec("INSERT INTO table VALUES (?)", item)
}

// ✅ 推荐:批量写入
batch := make([]any, 0, 1000)
for i, item := range items {
    batch = append(batch, item)
    if (i+1) % 1000 == 0 {
        db.Exec("INSERT INTO table VALUES (?)", batch)
        batch = batch[:0]
    }
}

4.3 连接池

// 数据库连接池
db, _ := sql.Open("mysql", dsn)
db.SetMaxOpenConns(100)
db.SetMaxIdleConns(50)
db.SetConnMaxLifetime(time.Hour)

// HTTP 连接池
transport := &http.Transport{
    MaxIdleConns:        100,
    MaxIdleConnsPerHost: 10,
    IdleConnTimeout:     90 * time.Second,
}
client := &http.Client{Transport: transport}

五、CPU 优化

5.1 减少锁竞争

// ❌ 避免:全局锁
var mu sync.Mutex
func update() {
    mu.Lock()
    defer mu.Unlock()
    // ...
}

// ✅ 推荐:分段锁
type ShardedMap struct {
    shards [16]struct {
        sync.RWMutex
        data map[string]int
    }
}

func (sm *ShardedMap) Get(key string) int {
    shard := sm.getShard(key)
    shard.RLock()
    defer shard.RUnlock()
    return shard.data[key]
}

5.2 使用原子操作

// ❌ 避免:锁保护计数器
var (
    mu    sync.Mutex
    count int
)

func inc() {
    mu.Lock()
    count++
    mu.Unlock()
}

// ✅ 推荐:原子操作
var count atomic.Int64

func inc() {
    count.Add(1)
}

5.3 避免逃逸

// ❌ 避免:堆分配
func create() *LargeStruct {
    return &LargeStruct{} // 逃逸到堆
}

// ✅ 推荐:栈分配
func process() {
    var s LargeStruct // 栈上分配
    use(s)
}

六、最佳实践

6.1 性能测试流程

1. 建立基准
2. 性能分析(pprof)
3. 识别瓶颈
4. 优化实现
5. 验证效果
6. 回归测试

6.2 优化原则

1. 先测量,后优化
2. 优先优化热点代码
3. 保持代码可读性
4. 避免过早优化
5. 验证优化效果

6.3 监控指标

// 关键指标
metrics := map[string]float64{
    "latency_p99":    latencyP99,
    "throughput":     throughput,
    "error_rate":     errorRate,
    "memory_usage":   memoryUsage,
    "gc_pause":       gcPause,
}

七、总结

Go 性能优化核心要点:

方面优化方法工具
内存减少分配、复用、预分配pprof
并发控制并发数、工作池goroutine
I/O缓冲、批量、连接池bufio
CPU减少锁、原子操作atomic

性能优化是系统工程,需要持续监控和迭代改进。


分享这篇文章到:

上一篇文章
Few-Shot 样本工程设计
下一篇文章
熟悉和陌生你选择什么?