golang: 模仿 VictoriaMetrics 中的做法,通过把局部变量放在自定义 Context 对象中来做到hot path 的 0 alloc
作者:张富春(ahfuzhang),转载时请注明作者和引用链接,谢谢!
使用 benchmark 压测过程中通常会出现这样的信息:
go test -v -bench=. -benchmem f1 10000 120860 ns/op 2433 B/op 28 allocs/op f2 10000 120288 ns/op 2288 B/op 26 allocs/op
可以看见 f1 在每次运行都产生了 28 次内存分配。
gc 通常是 golang 最大的性能杀手,减少内存分配对性能提升非常明显。
可以把程序区分为 hot path
和 非hot path
,hot path 即运行最频繁,消耗时间最多的程序执行路径。VictoriaMetrics 的作者 Valyala 建议在 Hot path 上做到 0 alloc.
然而,必须需要在函数间传递的对象指针,必然需要引起 alloc。减少内存分配的一个办法是 sync.Pool,但是如果在 A 函数中使用 sync.Pool.Get, 而在 B 函数中使用 sync.Pool.Put,这样的程序流程比较混乱,不容易维护。且,当存在大量的不同对象时,其 sync.Pool 的种类也很多;sync.Pool 还有全局锁,会影响程序的并发性。
VictoriaMetrics 中大量的使用了这样的技巧:
1. 定义自己的 Context 对象
type MyContext struct{ } // 业务函数的第一个参数都是 MyContext func BizFunc1(ctx *MyContext){} func BizFunc2(ctx *MyContext){}
2. 所有在函数间传递的变量(引起栈逃逸的),都定义在 MyContext 中
type MyContext struct{ tempBuffer []byte } // 如果函数都依赖 tempBuffer, 把局部变量定义到 MyContext 中 func BizFunc1(ctx *MyContext){ ctx.tempBuffer = append(ctx.tempBuffer, "str1"...) } func BizFunc2(ctx *MyContext){ ctx.tempBuffer = append(ctx.tempBuffer, "str2"...) }
3. MyContext 本身从 sync.Pool 中获取
var poolOfMyContext = sync.Pool{ New: func() interface{}{ return &MyContext{} } } // 业务入口函数 func BizEntrance(){ ctx := poolOfMyContext.Get().(*MyContext) defer poolOfMyContext.Put(ctx) // callBizFunc(ctx) // 业务逻辑函数 }
4. MyContext 对象提供 Reset() 方法
对分配好的各种缓冲区重用,避免反复分配。
func (c *MyContext) Reset() { c.tempBuffer = c.tempBuffer[:0] // 重用分配好的空间 } // 业务入口函数 func BizEntrance(){ ctx := poolOfMyContext.Get().(*MyContext) ctx.Reset() // 需要清空内容,避免上次的数据干扰运行结果 defer poolOfMyContext.Put(ctx) // callBizFunc(ctx) // 业务逻辑函数 }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异
2022-07-19 【笔记】vm-storage的go profile调用图表(没什么实际意义,就是为了做笔记)
2022-07-19 【记录一个问题】vm-select和vm-storage均无法做并行查询