Go性能调优|青训营笔记

65 阅读3分钟

这是我参与「第五届青训营 」笔记创作活动的第3天

Go高质量编码

编码规范

  • 代码格式
  • 注释
  • 命名规范
  • 控制流程
  • 错误和异常处理:recover()用于处理panic,只能在被defer的函数中使用,嵌套无法生效,只在当前goroutine生效

多个defer语句是后进先出

性能优化建议

Benchmark

编写好示例代码:

 //fib.go
func Fib(n int) int {
    if n < 2 {
        return n
    }
    return Fib(n-1) + Fib(n-2)
}
​
//fib_test.go
func BenchmarkFib(b *testing.B) {
    for n := 0; n < b.N; n++ {
        Fib(10)
    }
}

然后输入指令

go test -bench .

结果如下:

uTools_1673940888692

其中:

  • BenchmarkFib-8:前面是函数名,后面是GOMAXPROCS(Go1.5后默认为CPU核数)
  • 4777920:总共执行次数,即b.N的值
  • 253.6 ns/op:每次执行花费253.6ns

Slice

预分配内存

尽可能在使用make()初始化切片时提供容量信息

uTools_1673941197746

大内存未释放

  • 在已有切片基础上创建切片,不会创建新的底层数组

  • 场景:

    • 原切片较大,代码在原切片基础上新建小切片
    • 原底层数组在内存中有引用,得不到释放
  • 可使用copy代替re-slice

Map

预分配内存

uTools_1673941852766

字符串处理

使用strings.Builder

  • 常见拼接方式:

    uTools_1673942048693

uTools_1673942119574

性能差异:

uTools_1673942141978

原因:

  • 每次使用+会开辟新的空间,会涉及内存分配,性能更差一些
  • bytes.Buffer转化为字符串时,重新分配了一块空间
  • strings.Builder 直接将底层[]byte转化为了字符串类型返回
  • 使用预分配可以进一步提高性能:uTools_1673942421740

空结构体

使用空结构体节省内存

  • 空结构体struct{}不占据任何的内存空间

  • 可作为任何场景下的占位符使用

    • 节省资源
    • 空结构体本身具有很强的语义,即这里不需要任何值,仅作为占位符

Atomic包

如何使用atomic包:

type atomicCounter struct {
    i int32
}
​
func AtomicAddOne(c *atomicCounter) {
    atomic.AddInt32(&c.i, 1)
}

原子操作比加锁性能好很多

原因:

  • 锁是通过操作系统来实现,属于系统调用
  • atomic是通过硬件实现,效率比锁高
  • sync.Mutex应该用来保护一段逻辑,不仅仅用于保护一个变量
  • 对于非数值操作,可以使用atomic.Value,能承载一个interface{}

性能调优实战

简介

性能优化原则

  • 要依靠数据而不是猜测
  • 要定位最大瓶颈而不是细枝末节
  • 不要过早优化
  • 不要过度优化

pprof

场景

  • 希望知道应用在什么地方耗费了多少CPU、Memory
  • pprof是用于可视化和分析性能数据的工具

功能简介

uTools_1673943420435

实战

[项目代码]  github.com/wolfogre/go… 

使用命令采集数据
go tool pprof http://localhost:6060/debug/pprof/profile?seconds=10

uTools_1673944731106

输入top命令查看cup占用情况:uTools_1673944780255

uTools_1673944797898

可以发现是tiger.Eat方法占用最大,我们来看一下代码:

uTools_1673944922998

是一个巨大的空循环,这就把问题排查出来了

思考
  • Flat == Cum时,函数中没有调用其他函数
  • Flat == 0时,函数中只有其他函数的调用
Heap内存分析
 go tool pprof -http=localhost:8080 "http://localhost:6060/debug/pprof/heap"

uTools_1673947251701

Gorounine泄露
 go tool pprof -http=localhost:8080 "http://localhost:6060/debug/pprof/goroutine"

uTools_1673947455535

火焰图:

uTools_1673947562016

  • 由上到下表示调用顺序
  • 每一块代表一个函数越长代表CUP占用时间越长
  • 火焰图是动态的,支持点击块进行分析