Benchmark使用
Go语言的基准测试(Benchmark)是一种用于测量代码性能的方法。基准测试可以帮助开发者了解特定代码段的执行效率,比如函数调用的耗时、循环的执行速度等。通过基准测试,你可以优化代码以提高程序的整体性能。
基准测试的基本结构
基准测试使用 testing 包中的 Benchmark 函数来定义。一个典型的基准测试函数看起来像这样:
import (
"testing"
)
func BenchmarkExample(b *testing.B) {
// 这里放置要测试的代码
for i := 0; i < b.N; i++ {
// 测试这段代码的性能
}
}
在这个例子中,b.N 是一个由测试框架设置的计数器,表示测试函数应该运行的次数。测试框架会自动调整这个值,直到测试的时间足够长,以便获得准确的性能数据。
如何运行基准测试
要运行基准测试,可以使用 go test 命令加上 -bench 标志。例如,如果你有一个名为 example_test.go 的文件,其中包含了基准测试函数,你可以这样运行它:
go test -bench=.
如果你想运行特定的基准测试,可以指定基准测试函数的名字:
go test -bench=BenchmarkExample
基准测试的例子
假设我们有一个简单的函数,我们想要测试它的性能:
package main
import (
"testing"
)
// 这是我们要测试的函数
func Add(a, b int) int {
return a + b
}
// 基准测试函数
func BenchmarkAdd(b *testing.B) {
for i := 0; i < b.N; i++ {
Add(1, 2)
}
}
在这个例子中,BenchmarkAdd 函数会重复调用 Add 函数 b.N 次,并记录下总的执行时间。当你运行基准测试时,你会看到类似如下的输出:
BenchmarkAdd-8 1000000000 0.00 ns/op
PASS
ok command-line-arguments 0.542s
这里的输出表明,BenchmarkAdd 函数被调用了 10 亿次,每次调用的平均时间大约是 0.00 纳秒。
Slice
slice预分配内存
尽可能在使用make()初始化切片时提供容量信息
Map
map预分配内存
分析:
不断向map中添加元素的操作会触发map扩容
提前分配好空间可以减少内存拷贝和Rehash的消耗
建议根据实际需求提前预估好需要的空间
字符串处理
使用strings.Builder 使用 + 拼接性能最差, strings.Builder, bytes.Buffer相近,strings.Buffer 更快
分析: 字符串在Go语言中是不可变类型,占用内存大小是固定的 使用 + 每次都会重新分配内存 string.Builder, bytes.Buffer 底层都是[]byte数组 内存扩容策略,不需要每次拼接重新分配内存
空结构体
使用空结构体节省内存
空结构体struct{}实例不占据任何的内存空间
可作为各种场景下的占位符使用
节省资源
空结构体本身具备很强的语义,即这里不需要任何值,仅作为占位符
示例
作为通道的消息类型
当通道用于信号传递,而不需要传递任何数据时,可以使用空结构体。
func main() {
// 创建一个通道,通道的消息类型为空结构体
ch := make(chan struct{})
// 启动一个goroutine,模拟长时间运行的任务
go func() {
time.Sleep(2 * time.Second)
close(ch) // 任务完成,关闭通道
}()
// 等待任务完成
<-ch fmt.Println("Task completed")
}
atomic包
使用atomic包 锁的实现是通过硬件实现,属于系统调用
atomic操作是通过硬件实现,效率比锁高
sync.Mutex应该用来保护一段逻辑,不仅仅用于保护一个变量
对于非数值操作,可以使用atomic.Value,能承载一个interface{}