性能优化
本章介绍 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
最佳实践
- 先测量,后优化:使用 pprof 找出瓶颈
- 减少内存分配:使用对象池和预分配
- 优化并发:减少锁竞争,控制 goroutine 数量
- 优化 I/O:使用缓冲和批量操作
- 选择合适算法:根据场景选择最优算法
- 避免过早优化:保持代码简洁
总结
- 使用 pprof 分析性能
- 减少内存分配
- 优化并发性能
- 优化 I/O 操作
- 选择合适算法
- 先测量后优化
下一章:内存管理