news 2026/6/26 8:36:58

ahfuzhang

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
ahfuzhang

最近看到一个非常棒的 protobuf 的库:github.com/planetscale/vtprotobuf
其性能非常强悍,我自己写的版本始终没干过它。(在我的新版推出以前)vtprotobuf 可以算是 golang 领域最快的 protobuf 库。

为什么我就比不过它呢?我看到了这样的看不懂的代码:

func (m *Child) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if len(m.ChildName) > 0 { i -= len(m.ChildName) copy(dAtA[i:], m.ChildName) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.ChildName))) i-- dAtA[i] = 0x12 } if m.ChildId != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.ChildId)) i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil }

可以发现,这个库的特点是:先对数组的尾部赋值,然后下标向前偏移,然后再对数组首部进行赋值
难道这样就会变快?

Yes!
下面我就拆解一下变快的原因:

先看下面的两个函数:

func f1(arr []byte) { arr[0] = 1 arr[9] = 2 } func f2(arr []byte) { arr[9] = 2 arr[0] = 1 }

功能完全一样,只是顺序不同。

下面用命令行来检查数组越界检查:

go tool compile -d=ssa/check_bce/debug=1 bce.go

可以发现:

func f1(arr []byte) { arr[0] = 1 // 仍有 bounds check arr[9] = 2 // 仍有 bounds check } func f2(arr []byte) { arr[9] = 2 // 仍有 bounds check arr[0] = 1 // 没有 Found,说明这个检查被消掉了 }

由此说明:如果先出现了比较大的下标,再出现小的下标,那么编译器就能推断后续的数组访问一定没越界,由此便不再产生越界检查的代码。

从 golang 源码本身也能发现证据:
Go 编译器源码证据主要在 cmd/compile/internal/ssa/prove.go。OpIsInBounds 表示一次下标越界检查;当它为真时,编译器会学习到 0 <= index < length。源码注释直接写了:对于 OpIsInBounds,正分支会学习 signed 域里的 0 <= a0 < a1,以及 unsigned 域里的 a0 < a1,然后调用 ft.update 记录 index 和 length 之间的关系。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/26 8:34:26

Codex Skills 使用与配置教程

使用场景 Codex Skills 出问题&#xff0c;最常见的不是“模型不会写”&#xff0c;而是“规则没吃进去”&#xff1a;明明已经接上了接口&#xff0c;结果它还是按默认方式改代码、跑测试、写说明。先别急着改提示词&#xff0c;先查三件事&#xff1a;技能文件有没有被加载、…

作者头像 李华