Go语言通过testing包的Benchmark函数测量性能,需定义以Benchmark开头、参数为*testing.B的函数;2. 示例中测试字符串拼接函数性能,使用b.ResetTimer重置计时,循环执行i次以评估每操作耗时。

在Go语言开发中,性能优化离不开可靠的测试手段。Go内置的
testing包提供了benchmark功能,能帮助我们准确测量函数的执行时间、内存分配和GC情况。合理使用benchmark,可以为关键路径的性能调优提供数据支持。
编写基本的Benchmark函数
Benchmark函数写在
_test.go文件中,函数名以
Benchmark开头,参数类型为
*testing.B。运行时,Go会自动多次迭代该函数,计算每操作耗时。 示例:
假设有一个字符串拼接函数:
func ConcatStrings(strs []string) string {
var result string
for _, s := range strs {
result += s
}
return result
}
对应的benchmark测试如下:
立即学习“go语言免费学习笔记(深入)”;
func BenchmarkConcatStrings(b *testing.B) {
strs := []string{"a", "b", "c", "d", "e"}
b.ResetTimer()
for i := 0; i < b.N; i++ {
ConcatStrings(strs)
}
}
b.N是系统自动设定的迭代次数,Go会不断调整它,直到获得稳定的统计结果。
对比不同实现方式的性能
通过编写多个benchmark函数,可以横向比较不同算法或实现的性能差异。
比如用
strings.Join重写拼接逻辑:
func JoinStrings(strs []string) string {
return strings.Join(strs, "")
}
添加对应的benchmark:
func BenchmarkJoinStrings(b *testing.B) {
strs := []string{"a", "b", "c", "d", "e"}
b.ResetTimer()
for i := 0; i < b.N; i++ {
JoinStrings(strs)
}
}
运行命令:
go test -bench=.
输出类似:
BenchmarkConcatStrings-8 10000000 150 ns/op BenchmarkJoinStrings-8 20000000 80 ns/op
可见
strings.Join比字符串相加更快,且更节省内存。
控制输入规模与避免编译器优化
为了模拟真实场景,可以在benchmark中动态调整输入大小。
例如测试不同长度切片的表现:
func BenchmarkConcatStrings_10(b *testing.B) { benchConcat(b, 10) }
func BenchmarkConcatStrings_100(b *testing.B) { benchConcat(b, 100) }
func benchConcat(b *testing.B, size int) {
strs := make([]string, size)
for i := range strs {
strs[i] = "x"
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
ConcatStrings(strs)
}
}
另外,如果函数返回值未被使用,编译器可能直接优化掉调用。可通过
b.ReportMetric或赋值给
blackhole变量避免:
var result string result = ConcatStrings(strs)
或使用
runtime.GC强制触发GC,观察内存压力:
b.Run("WithGC", func(b *testing.B) {
for i := 0; i < b.N; i++ {
ConcatStrings(strs)
if i%100 == 0 {
runtime.GC()
}
}
})
分析内存分配与性能瓶颈
加上
-benchmem参数可查看内存分配情况:
go test -bench=. -benchmem
输出中包含:
- Allocated bytes per operation (B/op):每次操作分配的字节数
- Allocations per operation (allocs/op):每次操作的内存分配次数
理想情况下应尽量减少这两项数值。例如,使用
strings.Builder可以进一步优化内存:
func BuildString(strs []string) string {
var sb strings.Builder
for _, s := range strs {
sb.WriteString(s)
}
return sb.String()
}
其benchmark通常会显示更低的内存分配和更高的吞吐量。
基本上就这些。掌握benchmark写法后,可以持续监控关键函数的性能变化,尤其在重构或升级依赖时非常有用。











