theconsensuslabs/noalloc

开发工具

检查 Go 调用树中的堆分配,并提供固定容量容器与免分配 JSON 辅助工具。

103
1
Go
MIT
创建于 3 天前

noalloc 是一个 Go 工具链补充工具,面向已经写出免分配代码、希望代码变更后仍保持免分配的团队。它不只像 testing.AllocsPerRun 那样测一组测试输入,而是从标记的入口函数沿整棵调用树查找,报告每一行可能分配的代码,让“不分配”成为可审查的属性,而不是靠习惯维持。作者称它已在自己的公司用于生产,但 README 明确说它由 AI 编写、处于实验阶段,可能大幅变化。

检查怎么用

用 //noalloc:root 标记函数后,noalloc CLI 会检查该函数及其调用的一切。发现项会给出文件、行号、函数和原因,例如 map 写入,或逃逸到堆的字符串拼接;加 -report 还会列出导致分配的调用链。发现分配时退出码为 1,无法运行时为 2。可以用 //noalloc:exempt <reason> 豁免某行,并把豁免清单落盘做代码审查比对,豁免范围一旦扩大就会暴露。

三个指令决定判定范围://noalloc:fixed 标记方法不会增长其切片的类型,//noalloc:append 标记类似 append 的函数,-scratch pkg.Type 声明类型的字段是复用内存。结束 root 调用的错误或 panic 路径允许每次调用分配一次,判空后首次初始化的设置也允许;循环中继续执行而非终止调用的失败则按普通路径检查。已有分配的代码可通过 baseline 文件逐步接入,之后只对新分配报错。

CLI 要求 Go 1.27 或更高版本。它解析 go build -gcflags=-m 的输出,因此 Go 版本之间该输出的变化会影响结果:README 建议在 go.mod 中固定工具链,并在升级后重跑。

固定容量容器与编解码

bounded 包(bounded)提供 Buf、Vec、Slice、Map,都有固定最大容量。超出容量的写入不生效,只把容器标记为满,因此请求结束时检查一次即可。每个请求从带预算的 Region 借用共享 Pool 的块,请求结束时释放归还。README 配套讲了明确的准入规划:请求获准后再预留,池按同时获准的请求量来定,并为预算耗尽准备处理方案。

JSON 包(encoding/json)用 jsongen 命令为类型生成免分配的 WriteJSON/ReadJSON 方法,不支持的 map 和 interface 会被拒绝。README 报告它在 benchmark 的 feed 上比 goccy/go-json 慢,但每次 feed 解码零分配;数据来自 Apple M4、Go 1.27.0,且在带负载的条件下测得,适合横向比较,不宜当成绝对性能。bounded 的 pool 和 map 也明确比 sync.Pool 和 Go 原生 map 慢,换来的是有界的内存占用。另有 HTML 与 URL 转义器的 append 版本,仅覆盖 html/template 转义器中的一部分。

采用前需要知道的限制

noalloc 不验证白名单和任何豁免内容,直接信任它们;通过函数值的间接调用无法解析,会被报告为问题。接口调用会检查它找到的所有实现,因此可能出现误报,例如 io.Writer.Write。与编译产物的交叉检查也有缺口(其他包内联进来的调用、同时含未检查代码的行、无法归类的 runtime 调用),并且只覆盖一种构建配置。在新工具链或依赖集合上首次运行时显著更慢,README 给出的数字是 39 秒对 4 秒。

许可证为 MIT,部分改编自 Go 源码的代码保留 Go 的 BSD 许可证(LICENSE)。

本文由 AI 根据项目 README 生成,更新于 2026年10月30日,以项目官方文档为准。

Hacker News 讨论

noalloc: Tooling and libraries to enforce zero heap allocations in Golang
▲ 75 · 47 条评论 · 2026年10月28日