Skip to content
On this page

性能优化

本章介绍 Go 程序性能优化的技巧和工具。

性能分析工具

pprof

go
package main

import (
    "log"
    "net/http"
    _ "net/http/pprof"
)

func main() {
    go func() {
        log.Println(http.ListenAndServe("localhost:6060", nil))
    }()
    
    // 你的代码...
    select {}
}

CPU 分析

bash
# 启动程序
go run main.go

# 采集 CPU 数据
go tool pprof http://localhost:6060/debug/pprof/profile

# 查看结果
(pprof) top
(pprof) list functionName
(pprof) web

内存分析

bash
# 堆分析
go tool pprof http://localhost:6060/debug/pprof/heap

# 对象分配
go tool pprof http://localhost:6060/debug/pprof/allocs

Goroutine 分析

bash
# Goroutine 数量
go tool pprof http://localhost:6060/debug/pprof/goroutine

# 阻塞的 Goroutine
go tool pprof http://localhost:6060/debug/pprof/block

内存优化

减少内存分配

go
// 不推荐:频繁分配
func concatenate() string {
    result := ""
    for i := 0; i < 1000; i++ {
        result += "hello"
    }
    return result
}

// 推荐:使用 strings.Builder
func concatenateOptimized() string {
    var builder strings.Builder
    for i := 0; i < 1000; i++ {
        builder.WriteString("hello")
    }
    return builder.String()
}

对象池

go
var bufferPool = sync.Pool{
    New: func() interface{} {
        return new(bytes.Buffer)
    },
}

func processData(data []byte) {
    buf := bufferPool.Get().(*bytes.Buffer)
    defer func() {
        buf.Reset()
        bufferPool.Put(buf)
    }()
    
    buf.Write(data)
    // 处理数据...
}

预分配容量

go
// 不推荐:频繁扩容
func createSlice(n int) []int {
    slice := []int{}
    for i := 0; i < n; i++ {
        slice = append(slice, i)
    }
    return slice
}

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

并发优化

减少锁竞争

go
// 不推荐:全局锁
var mu sync.Mutex
var data map[string]int

func set(key string, value int) {
    mu.Lock()
    data[key] = value
    mu.Unlock()
}

// 推荐:分片锁
type ShardedMap struct {
    shards []*shard
}

type shard struct {
    mu   sync.RWMutex
    data map[string]int
}

func NewShardedMap(n int) *ShardedMap {
    shards := make([]*shard, n)
    for i := 0; i < n; i++ {
        shards[i] = &shard{data: make(map[string]int)}
    }
    return &ShardedMap{shards: shards}
}

func (sm *ShardedMap) getShard(key string) *shard {
    hash := fnv.New32a()
    hash.Write([]byte(key))
    return sm.shards[hash.Sum32()%uint32(len(sm.shards))]
}

func (sm *ShardedMap) Set(key string, value int) {
    shard := sm.getShard(key)
    shard.mu.Lock()
    shard.data[key] = value
    shard.mu.Unlock()
}

使用 Channel 而非锁

go
// 不推荐:使用锁
type Counter struct {
    mu    sync.Mutex
    count int
}

func (c *Counter) Increment() {
    c.mu.Lock()
    defer c.mu.Unlock()
    c.count++
}

// 推荐:使用 Channel
type ChannelCounter struct {
    ch chan func()
}

func NewChannelCounter() *ChannelCounter {
    cc := &ChannelCounter{
        ch: make(chan func(), 100),
    }
    go cc.run()
    return cc
}

func (cc *ChannelCounter) run() {
    var count int
    for fn := range cc.ch {
        fn()
        count++
    }
}

func (cc *ChannelCounter) Increment() {
    cc.ch <- func() {}
}

控制 Goroutine 数量

go
// 不推荐:无限制创建
func processItems(items []Item) {
    for _, item := range items {
        go process(item)
    }
}

// 推荐:使用工作池
func processItemsOptimized(items []Item) {
    const workers = 10
    jobs := make(chan Item, len(items))
    
    for i := 0; i < workers; i++ {
        go func() {
            for item := range jobs {
                process(item)
            }
        }()
    }
    
    for _, item := range items {
        jobs <- item
    }
    close(jobs)
}

I/O 优化

缓冲 I/O

go
// 不推荐:频繁小写入
func writeFile(filename string) error {
    file, err := os.Create(filename)
    if err != nil {
        return err
    }
    defer file.Close()
    
    for i := 0; i < 1000; i++ {
        file.WriteString("hello\n")
    }
    return nil
}

// 推荐:使用缓冲
func writeFileOptimized(filename string) error {
    file, err := os.Create(filename)
    if err != nil {
        return err
    }
    defer file.Close()
    
    writer := bufio.NewWriter(file)
    defer writer.Flush()
    
    for i := 0; i < 1000; i++ {
        writer.WriteString("hello\n")
    }
    return nil
}

批量操作

go
// 不推荐:逐个处理
func processItems(items []Item) {
    for _, item := range items {
        process(item)
    }
}

// 推荐:批量处理
func processItemsBatch(items []Item) {
    const batchSize = 100
    for i := 0; i < len(items); i += batchSize {
        end := i + batchSize
        if end > len(items) {
            end = len(items)
        }
        processBatch(items[i:end])
    }
}

算法优化

选择合适的算法

go
// 不推荐:O(n^2)
func contains(slice []int, target int) bool {
    for _, item := range slice {
        if item == target {
            return true
        }
    }
    return false
}

// 推荐:O(log n) 使用 map
type IntSet struct {
    data map[int]bool
}

func NewIntSet() *IntSet {
    return &IntSet{data: make(map[int]bool)}
}

func (s *IntSet) Add(item int) {
    s.data[item] = true
}

func (s *IntSet) Contains(item int) bool {
    return s.data[item]
}

避免不必要的计算

go
// 不推荐:重复计算
func process(data []int) {
    for i := 0; i < len(data); i++ {
        for j := 0; j < len(data); j++ {
            // 处理...
        }
    }
}

// 推荐:缓存长度
func processOptimized(data []int) {
    n := len(data)
    for i := 0; i < n; i++ {
        for j := 0; j < n; j++ {
            // 处理...
        }
    }
}

字符串优化

避免字符串拼接

go
// 不推荐
func buildString() string {
    result := ""
    for i := 0; i < 1000; i++ {
        result += "hello"
    }
    return result
}

// 推荐
func buildStringOptimized() string {
    var builder strings.Builder
    for i := 0; i < 1000; i++ {
        builder.WriteString("hello")
    }
    return builder.String()
}

使用字节切片

go
// 不推荐:字符串操作
func processString(s string) string {
    result := ""
    for _, c := range s {
        if c >= 'a' && c <= 'z' {
            result += string(c)
        }
    }
    return result
}

// 推荐:字节操作
func processStringOptimized(s string) string {
    var builder strings.Builder
    for _, c := range s {
        if c >= 'a' && c <= 'z' {
            builder.WriteRune(c)
        }
    }
    return builder.String()
}

性能测试

基准测试

go
func BenchmarkConcatenate(b *testing.B) {
    for i := 0; i < b.N; i++ {
        concatenate()
    }
}

func BenchmarkConcatenateOptimized(b *testing.B) {
    for i := 0; i < b.N; i++ {
        concatenateOptimized()
    }
}

内存分析

bash
# 运行基准测试并分析内存
go test -bench=. -benchmem

# 生成内存分析文件
go test -bench=. -memprofile=mem.out

# 查看内存分析
go tool pprof mem.out

最佳实践

  1. 先测量,后优化:使用 pprof 找出瓶颈
  2. 减少内存分配:使用对象池和预分配
  3. 优化并发:减少锁竞争,控制 goroutine 数量
  4. 优化 I/O:使用缓冲和批量操作
  5. 选择合适算法:根据场景选择最优算法
  6. 避免过早优化:保持代码简洁

总结

  • 使用 pprof 分析性能
  • 减少内存分配
  • 优化并发性能
  • 优化 I/O 操作
  • 选择合适算法
  • 先测量后优化

下一章:内存管理

基于 MIT 许可发布