在Golang开发中,内存使用基准测试是评估程序性能的重要手段,通过内置的testing包可以快速实现内存相关的基准测试,精准获取函数运行时的内存分配情况。

Golang内存基准测试基础
Golang的基准测试依托于testing包实现,内存相关的基准测试在普通基准测试的基础上,会自动统计内存分配的次数和总字节数。基准测试函数的命名需要遵循BenchmarkXxx的格式,其中Xxx为自定义的名称,函数接收*testing.B类型的参数。
基础内存基准测试示例
下面是一个简单的内存基准测试示例,测试字符串拼接操作的内存使用情况:
package main
import (
"strings"
"testing"
)
// BenchmarkStringConcat 测试字符串拼接的内存使用
func BenchmarkStringConcat(b *testing.B) {
// 重置计时器,排除前置准备代码的耗时和内存分配
b.ResetTimer()
for i := 0; i < b.N; i++ {
var s string
for j := 0; j < 100; j++ {
s += strings.Repeat("a", 10)
}
}
}
运行该基准测试时,使用go test -bench=. -benchmem命令,其中-benchmem参数用于开启内存统计,输出的结果中会包含每操作的内存分配字节数和分配次数。
常用参数与结果解读
运行内存基准测试时,常用的参数和输出结果的含义如下:
- -bench=.:运行当前包下所有的基准测试函数
- -benchmem:开启内存分配统计,输出内存相关的指标
- -benchtime=5s:设置每个基准测试的运行时间,默认是1秒
- -count=3:设置每个基准测试的运行次数,取多次结果的平均值
上述示例的运行结果可能如下:
BenchmarkStringConcat-8 10000 105678 ns/op 101248 B/op 100 allocs/op
结果各字段的含义:
| 字段 | 含义 |
|---|---|
| BenchmarkStringConcat-8 | 基准测试函数名,8表示GOMAXPROCS的值为8 |
| 10000 | 基准测试的总迭代次数,即b.N的最终值 |
| 105678 ns/op | 每次操作的平均耗时,单位为纳秒 |
| 101248 B/op | 每次操作的平均内存分配字节数 |
| 100 allocs/op | 每次操作的平均内存分配次数 |
进阶内存基准测试技巧
排除前置代码的内存干扰
如果基准测试函数中有前置的准备代码,这些代码的内存分配会被计入统计,此时需要使用b.ResetTimer()重置计时器,或者使用b.StopTimer()和b.StartTimer()暂停和恢复计时:
func BenchmarkWithPrepare(b *testing.B) {
// 前置准备代码,会分配内存
data := make([]int, 1000)
// 停止计时,排除准备代码的耗时和内存分配
b.StopTimer()
// 可以在这里做一些不需要计入统计的操作
b.StartTimer()
for i := 0; i < b.N; i++ {
_ = append(data, i)
}
}
测试不同场景的内存使用
可以通过子测试的方式,测试同一函数在不同输入参数下的内存使用情况:
func BenchmarkMemoryAlloc(b *testing.B) {
// 测试不同切片初始容量的内存分配情况
b.Run("cap_10", func(b *testing.B) {
for i := 0; i < b.N; i++ {
s := make([]int, 0, 10)
for j := 0; j < 100; j++ {
s = append(s, j)
}
}
})
b.Run("cap_100", func(b *testing.B) {
for i := 0; i < b.N; i++ {
s := make([]int, 0, 100)
for j := 0; j < 100; j++ {
s = append(s, j)
}
}
})
}
运行后可以看到不同初始容量下,内存分配次数和总字节数的差异,从而选择更优的实现方式。
注意事项
- 基准测试的迭代次数
b.N是由框架自动调整的,不需要手动设置,保证测试时间足够长以获得稳定结果 - 避免在基准测试中使用
fmt.Println等输出操作,这些操作会额外分配内存,干扰测试结果 - 多次运行基准测试取平均值,避免单次运行的偶然误差影响结果准确性
- 如果函数本身没有内存分配,输出中的内存相关指标可能为0,属于正常情况
内存基准测试的结果受运行环境、Golang版本等因素影响,不同环境下结果可能存在差异,建议在同一环境下对比不同实现的性能。