跳到主要内容

编译优化

问题

Go 编译器有哪些优化?开发者能做什么帮助编译器?

答案

编译器自动优化

优化说明
函数内联小函数直接展开到调用点
逃逸分析尽量在栈上分配
死代码消除移除不可达代码
常量折叠编译时计算常量表达式
bounds check elimination编译器证明安全后移除边界检查

函数内联

内联将函数体直接嵌入调用者,减少函数调用开销:

// 小函数自动内联
func add(a, b int) int { return a + b }

// 查看内联决策
// go build -gcflags="-m" ./...
// ./main.go:5: can inline add
// ./main.go:10: inlining call to add

禁止内联(Benchmark 时避免影响):

//go:noinline
func add(a, b int) int { return a + b }

边界检查消除

// 编译器会插入运行时边界检查 → 有开销
func sum(s []int) int {
total := 0
for i := 0; i < len(s); i++ {
total += s[i] // 每次访问都检查 i < len(s)
}
return total
}

// 帮助编译器消除检查
func sum(s []int) int {
total := 0
_ = s[len(s)-1] // bounds check hint
for i := 0; i < len(s); i++ {
total += s[i] // 编译器知道 i < len(s),跳过检查
}
return total
}

PGO(Profile-Guided Optimization)

# Go 1.21+ 支持
# 放 default.pgo 在项目根目录,编译时自动启用
go build -pgo=auto ./cmd/server

PGO 根据 CPU Profile 优化热路径的内联、devirtualization 等,通常提升 2-7%。

编译标志

# 查看逃逸分析
go build -gcflags="-m" ./...

# 查看 SSA 优化
GOSSAFUNC=funcName go build

# 减小二进制体积
go build -ldflags="-s -w"

常见面试问题

Q1: Go 有 JIT 吗?

答案没有。Go 是 AOT(Ahead-of-Time)编译,直接编译为机器码。优势是启动快、部署简单,劣势是无法做运行时热点优化。PGO 是编译时优化的补偿方案。

Q2: 如何让函数被内联?

答案

  • 函数体足够小(Go 编译器有内联预算)
  • 不包含 deferrecoverselect 等复杂结构
  • 不包含 for range 中的闭包
  • 可用 go build -gcflags="-m" 查看哪些函数被内联

相关链接