贝利信息

如何使用Golang测试序列化性能_Golang encoding/json Benchmark方法

日期:2026-01-21 00:00 / 作者:P粉602998670
靠谱的 json.Marshal Benchmark 需每次迭代新建数据并隔离内存生命周期:调用 b.ReportAllocs() 统计分配,b.ResetTimer() 排除初始化开销,不复用结构体或切片,避免缓存干扰与 GC 抖动。

为什么 encoding/json 的 Benchmark 容易失真

直接用 testing.Benchmarkjson.Marshaljson.Unmarshal 时,常见结果波动大、不可复现,甚至比实际运行快几倍。根本原因是:Go 的 benchmark 默认会复用变量、不强制 GC、忽略内存逃逸路径,而 JSON 序列化对内存分配和 GC 敏感度极高。

写一个靠谱的 json.Marshal Benchmark 示例

核心是让每次迭代都生成「新数据」+「强制隔离内存生命周期」。避免复用结构体指针或预分配切片。

func BenchmarkJSONMarshal(b *testing.B) {
	b.ReportAllocs()
	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		// 每次迭代构造全新数据,防止编译器优化或缓存复用
		data := struct {
			ID    int    `json:"id"`
			Name  string `json:"name"`
			Tags  []string `json:"tags"`
			Valid bool   `json:"valid"`
		}{
			ID:    i,
			Name:  "user-" + strconv.Itoa(i%1000),
			Tags:  []string{"go", "json", "perf"},
			Valid: true,
		}

		_, err := json.Marshal(data)
		if err != nil {
			b.Fatal(err)
		}
	}
}

对比 json.Unmarshal 时必须预热字节流

反序列化性能受输入字节流是否已驻留 L1/L2 缓存影响极大。若每次 json.Unmarshal 都从新分配的 []byte 开始,测的是内存拷贝 + 解析,不是纯解析。

var benchData []byte

func init() {
	data := struct{ Name string }{Name: "test"}
	var err error
	benchData, err = json.Marshal(data)
	if err != nil {
		panic(err)
	}
}

func BenchmarkJ

SONUnmarshal(b *testing.B) { b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { var v struct{ Name string } err := json.Unmarshal(benchData, &v) if err != nil { b.Fatal(err) } } }

真正影响线上性能的三个隐藏点

跑出漂亮 benchmark 数字不等于线上快。以下三点常被忽略,但决定 JSON 序列化是否成为瓶颈:

别只盯着 ns/op,先看 allocs/op 是否稳定、GCPause 是否突增、pprof 的 runtime.mallocgc 占比 —— 这些才是压测时真正该盯住的地方。