必须用 go test -bench 测字符串拼接性能,因其禁用编译器优化、提供稳定计时和内存统计;需用变量参与运算、预分配并调用 b.ResetTimer(),且必须加 -benchmem 关注 allocs/op 和 B/op。

直接用 go test -bench 测,别信文档或直觉;不控制变量、不用变量参与运算、不加 -benchmem,测出来全是假数据。
为什么必须用 go test -bench 而不是手写循环打点
Go 编译器对纯字面量拼接(如 "a" + "b" + "c")会做常量折叠,你手写一个 for 循环里跑 s += "x",很可能被整个优化掉,或者只执行一次——根本测不到真实开销。基准测试框架强制禁用这类优化,并提供稳定计时和内存统计入口。
- 必须用变量参与运算:比如从
[]string切片中取值、用strconv.Itoa(i)生成内容,否则编译器可能提前算出结果 - 所有待测函数接收相同参数签名,例如
func(b *testing.B, parts []string),避免输入差异干扰对比 - 初始化代码(如预分配
strings.Builder或构造切片)要放在b.ResetTimer()之前,否则计入耗时 - 绝对不要在
Benchmark函数里调用fmt.Println或日志,它们的 I/O 开销会掩盖真实拼接成本
-benchmem 比 ns/op 更关键
字符串拼接的瓶颈往往不在 CPU,而在堆分配次数(allocs/op)和单次分配字节数(B/op)。比如 result += s 在循环中可能显示 100ns/op,但 -benchmem 会暴露它每轮都分配新内存,100 次就是 100 次 alloc —— 这才是 GC 压力来源。
- 重点关注
allocs/op:理想情况是 0 或 1(预分配后 Builder 只分配一次底层数组) -
B/op应该接近最终字符串总长度:如果拼接 200KB 字符串却报告 800KB,说明有冗余拷贝(比如误用bytes.Buffer.String()) - 对比时横向看同一列:比如
strings.Builder和strings.Join的B/op差距,比单独看各自ns/op更能反映内存效率
常见陷阱:测了等于没测
很多“性能对比”博客跑出来的数据不可信,因为没隔离关键干扰项:
- 用
fmt.Sprintf("%s%s", a, b)测拼接,实际测的是格式解析开销,不是连接本身;换成fmt.Sprint(a, b)才接近真实串联成本 - 测试
strings.Join却把append到[]string的过程包进去,结果慢的其实是 slice 扩容,不是Join本身 - 没调
builder.Grow(n)就直接测strings.Builder,等于测的是“未优化的 Builder”,不能代表它的上限能力 - 在 goroutine 里复用同一个
strings.Builder并发写,不 panic 但结果错乱,ns/op看着快,实际是脏数据
一个最小可验证基准示例
真正有用的基准测试长这样:
parts
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











