基准测试应以真实负载为准,优先使用 testing.B;初始化须在 b.ResetTimer() 前完成,避免计入分配开销;slice 随机读快但中间增删需拷贝,append 容量突变引发 realloc;map 查找均摊 O(1),小数据时性能未必优于 slice。
用 go test -bench 测量 slice、map、sync.Map 的实际开销
基准测试不是看代码多“优雅”,而是看在真实负载下谁更扛压。Golang 自带的 testing.B 是唯一推荐起点,别手写计时器——它自动处理预热、多次运行、统计抖动。
常见错误是直接在 BenchmarkXxx 函数里做初始化:比如每次迭代都 make([]int, 1000),这会把内存分配时间全算进结果。正确做法是把初始化提到 b.ResetTimer() 之前,或者用 b.ReportAllocs() 分离观察分配次数。
-
slice随机读快,但插入/删除中间位置需拷贝;测append时注意容量突变(如从 1024→2048)会触发 realloc,拉高单次耗时 -
map查找平均 O(1),但小数据量([]struct{key,val} 线性扫描快,因为哈希计算+指针跳转有固定开销 -
sync.Map只在并发读多写少场景有优势;纯单协程下比原生map慢 3–5 倍,别无脑替换
避免 BenchmarkMapSet 类测试被编译器优化掉
Go 编译器看到没副作用的循环(比如只往 map 写值但不读),可能直接删掉整个循环。结果跑出来是 0 ns/op,这不是性能好,是测试失效。
强制保留计算的方法很简单:在循环末尾加一行 blackhole = m[key](blackhole 是全局变量或传入的指针),或者用 runtime.KeepAlive(m)。更稳妥的是让每次操作依赖前一次结果,例如 key = key * 31 + 1。