3.4. 性能优化技巧
3.4.1. 字符串优化
3.4.1.1. 使用 strings.Builder
// ❌ 每次拼接都分配新内存
func concat(strs []string) string {
var result string
for _, s := range strs {
result += s
}
return result
}
// ✅ 使用 Builder
func concat(strs []string) string {
var sb strings.Builder
for _, s := range strs {
sb.WriteString(s)
}
return sb.String()
}
// ✅ 预分配容量
func concat(strs []string) string {
var sb strings.Builder
size := 0
for _, s := range strs {
size += len(s)
}
sb.Grow(size)
for _, s := range strs {
sb.WriteString(s)
}
return sb.String()
}
3.4.1.2. 字符串与字节切片转换
// ❌ 产生拷贝
s := string(bytes)
b := []byte(str)
// ✅ 零拷贝转换(unsafe,谨慎使用)
import "unsafe"
func stringToBytes(s string) []byte {
return unsafe.Slice(unsafe.StringData(s), len(s))
}
func bytesToString(b []byte) string {
return unsafe.String(unsafe.SliceData(b), len(b))
}
3.4.2. 切片优化
3.4.2.1. 预分配容量
// ❌ 多次扩容
func createSlice(n int) []int {
var s []int
for i := 0; i < n; i++ {
s = append(s, i)
}
return s
}
// ✅ 预分配
func createSlice(n int) []int {
s := make([]int, 0, n)
for i := 0; i < n; i++ {
s = append(s, i)
}
return s
}
3.4.2.2. 避免切片引用导致的内存泄漏
// ❌ 小切片引用大数组,导致大数组无法被 GC
func getFirstElement(data []int) []int {
return data[:1] // 仍然引用原始大数组
}
// ✅ 复制数据
func getFirstElement(data []int) []int {
result := make([]int, 1)
copy(result, data[:1])
return result
}
3.4.2.3. 复用切片
// ✅ 使用 slice[:0] 复用底层数组
func processInPlace(data []int) []int {
result := data[:0]
for _, v := range data {
if v > 0 {
result = append(result, v)
}
}
return result
}
3.4.3. Map 优化
3.4.3.1. 预分配容量
// ❌ 动态扩容
m := make(map[string]int)
// ✅ 预分配
m := make(map[string]int, expectedSize)
3.4.3.2. 使用结构体 key 而非字符串
// ❌ 字符串 key 需要 hash 和比较
type cacheKey string
cache := make(map[cacheKey]Value)
// ✅ 固定大小的 key 更高效
type cacheKey struct {
userID int64
itemID int64
}
cache := make(map[cacheKey]Value)
3.4.4. 并发优化
3.4.4.1. 减少锁竞争
// ❌ 全局锁
type Counter struct {
mu sync.Mutex
count int
}
// ✅ 分片锁减少竞争
type ShardedCounter struct {
shards [256]struct {
mu sync.Mutex
count int
}
}
func (c *ShardedCounter) Inc(key string) {
shard := &c.shards[hash(key)%256]
shard.mu.Lock()
shard.count++
shard.mu.Unlock()
}
3.4.4.2. 使用 atomic 代替 Mutex
// ❌ 使用 Mutex
type Counter struct {
mu sync.Mutex
count int64
}
func (c *Counter) Inc() {
c.mu.Lock()
c.count++
c.mu.Unlock()
}
// ✅ 使用 atomic
type Counter struct {
count atomic.Int64
}
func (c *Counter) Inc() {
c.count.Add(1)
}
3.4.5. 内存分配优化
3.4.5.1. 使用 sync.Pool
var bufPool = sync.Pool{
New: func() interface{} {
return new(bytes.Buffer)
},
}
func process(data []byte) []byte {
buf := bufPool.Get().(*bytes.Buffer)
defer func() {
buf.Reset()
bufPool.Put(buf)
}()
// 使用 buf 处理数据
buf.Write(data)
result := make([]byte, buf.Len())
copy(result, buf.Bytes())
return result
}
3.4.5.2. 避免不必要的指针
// ❌ 指针增加 GC 压力
type Item struct {
Name *string
Value *int
}
// ✅ 值类型
type Item struct {
Name string
Value int
}
3.4.6. I/O 优化
3.4.6.1. 使用 bufio
// ❌ 每次读取都是系统调用
func readLines(filename string) ([]string, error) {
f, _ := os.Open(filename)
defer f.Close()
var lines []string
buf := make([]byte, 1)
for {
_, err := f.Read(buf)
// ...
}
return lines, nil
}
// ✅ 使用 bufio
func readLines(filename string) ([]string, error) {
f, _ := os.Open(filename)
defer f.Close()
var lines []string
scanner := bufio.NewScanner(f)
for scanner.Scan() {
lines = append(lines, scanner.Text())
}
return lines, scanner.Err()
}
3.4.6.2. 使用 io.Copy
// ❌ 手动复制
func copyFile(src, dst string) error {
data, _ := os.ReadFile(src) // 全部读入内存
return os.WriteFile(dst, data, 0644)
}
// ✅ 流式复制
func copyFile(src, dst string) error {
srcFile, _ := os.Open(src)
defer srcFile.Close()
dstFile, _ := os.Create(dst)
defer dstFile.Close()
_, err := io.Copy(dstFile, srcFile)
return err
}
3.4.7. JSON 优化
3.4.7.1. 使用 json.Decoder/Encoder
// ❌ 中间缓冲区
data, _ := json.Marshal(obj)
w.Write(data)
// ✅ 直接写入
json.NewEncoder(w).Encode(obj)
3.4.7.2. 使用 jsoniter 或 sonic
import jsoniter "github.com/json-iterator/go"
var json = jsoniter.ConfigCompatibleWithStandardLibrary
func encode(v interface{}) ([]byte, error) {
return json.Marshal(v)
}
3.4.8. 编译优化
3.4.8.1. 内联优化
// 小函数会被自动内联
//go:noinline // 禁用内联
func add(a, b int) int {
return a + b
}
3.4.8.2. 边界检查消除
// ❌ 每次访问都检查边界
func sum(s []int) int {
total := 0
for i := 0; i < len(s); i++ {
total += s[i]
}
return total
}
// ✅ 使用 range 避免边界检查
func sum(s []int) int {
total := 0
for _, v := range s {
total += v
}
return total
}
// ✅ 手动消除边界检查
func sum(s []int) int {
total := 0
_ = s[len(s)-1] // 边界检查提前
for i := 0; i < len(s); i++ {
total += s[i] // 不再检查
}
return total
}
3.4.9. 性能优化检查清单
检查清单
[ ] 使用
-race检查数据竞争[ ] 使用 pprof 找出热点
[ ] 使用
-benchmem检查内存分配[ ] 预分配切片和 map 容量
[ ] 使用 strings.Builder 拼接字符串
[ ] 使用 sync.Pool 复用对象
[ ] 减少锁的粒度和持有时间
[ ] 使用 bufio 进行 I/O
[ ] 检查逃逸分析结果
[ ] 使用基准测试验证优化效果