Go性能优化 | 青训营笔记

64 阅读2分钟

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

一、主要内容

  • 性能优化建议

二、详细知识点

1、Benchmark

当我们尝试优化Go代码的性能时,我们首先得知道当前的性能是什么样,性能表现需要实际数据衡量。Go语言标准库内置了testing框架,提供了基准测试的能力。

package main

import "testing"

func fib(n int) int {
	if n == 0 || n == 1 {
		return n
	}
	return fib(n - 2) + fib(n - 1)
}

func BenchmarkFib(b *testing.B) {
	for n := 0; n < b.N; n++ {
		fib(30)
	}
}
  • benchmark写在xxx_test.go文件中,和普通的单元测试一样。
  • Benchmark函数名以Benchmark开头,入参为b *testing.B。

2、Slice

  • slice预分配内存

尽可能在使用make()初始化切片时提供容量信息,预先分配内存可以减少动态扩容带来的开销,避免了多次内存分配,也可以减少空间增长而带来的数据迁移开销。

  • 大内存未释放

在已有切片基础上创建切片,不会创建新的底层数组,可以使用copy替代re-slice。

3、Map

  • map预分配内存 不断向map中添加元素的操作会触发map的扩容,提前分配好空间可以减少内存拷贝和Rehash的消耗。

4、字符串处理

  • 使用strings.Builder 使用 + 拼接性能最差,strings.Builder,bytes.Buffer相近,strings.Buffer更快。

分析

  • 字符串在Go语言中是不可变类型,占用内存大小是固定的
  • 使用+每次都会重新分配内存
  • strings.Builder,bytes.Buffer底层都是[]byte 数组
  • 内存扩容策略,不需要每次拼接都重新分配内存

5、空结构体

  • 使用空结构体节省内存

空结构体struct{}实例不占据任何的内存空间,可作为各种场景下的占位符使用,节省资源。空结构体本身具备很强的语义,即这里不需要任何值,仅作为占位符。

6、atomic包

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

三、课程总结

通过这节课可以了解到Go语言常见的性能陷阱,但不能一味地追求程序的性能,要在满足正确可靠、简洁清晰的质量要求的前提下提高程序性能。