2026-09-12 07:00:00
本文永久链接 – https://tonybai.com/2026/09/12/rust-complexity-debate-2026
大家好,我是Tony Bai。
【导读】
一位Rust开发者在r/rust发帖,列出了十几项正在讨论中的语言提案——命名参数、开放枚举、字段投影、Move/Destroy/Forget trait……他担心Rust正在一步步走上C++ “大杂烩”的老路。帖子迅速引爆社区,连Rust语言团队成员都亲自下场回应。这场争论,或许正是所有“成功的编程语言”都逃不开的宿命拷问。
【文章要点】

如果你关注编程语言圈,大概率已经刷到过这条帖子。
今日,Reddit的r/rust板块,一名用户发了一篇题为《My concerns about the future of Rust(我对Rust未来的担忧)》的帖子。
没有惊悚的标题,没有引战的措辞,开篇第一句甚至是:
“我爱Rust,但是……”
但就是这么一篇平静的帖子,硬是炸出了数百个赞和评论,甚至惊动了Rust语言团队的核心成员亲自下场解释。
争论的核心问题只有一个:Rust,会不会变成下一个C++?
楼主开门见山地表示,自己并不是反对某一条具体提案,而是担心把所有正在讨论的提案叠加在一起看时,Rust语言会呈现出令人不安的整体趋势。
他列出了一长串正在社区讨论、甚至已经有RFC(提案文档)在推进的语言特性,其中包括:
楼主的担忧很朴素:如果这些提案全部落地,Rust将新增几十个关键字和保留字,语法和语义复杂度会大幅上升。而Rust最打动人的地方之一,恰恰是“语言的每一部分都天然契合彼此”——这种正交性一旦被打破,Rust就有滑向“新时代C++”的风险。
他还特别点名了FFI(跨语言互操作)相关的提案:许多复杂改动的出发点是“更方便地对接C/C++代码”,但楼主直言,为了兼容日益过时的语言而牺牲Rust本身的简洁性,这在他看来是一种“本末倒置”。
帖子发出去不到半天,评论区就自然分成了几派。
第一派:技术限制派。一位用户指出,清单里大多数提案其实源于真实的技术局限,比如显式尾调用能让字节码解释器快出一大截;Drop语义控制解决的是“文件关闭失败”这种现实中确实无解的痛点。这一派的共识是:这些特性大概率只影响特定领域的开发者,平时“眼不见为净”。
第二派:反对复杂化派。这一派用户提出了针锋相对的观点,围绕Move/Destroy/Forget三件套展开了一场高质量拉锯战——有人认为它会让类型系统更复杂,也有人反驳说,这套机制恰恰是为了未来彻底淘汰std::pin这个公认的“复杂度重灾区”,长期看反而是在做减法。
第三派:坚定支持派。这类用户的发言也获得不少点赞,核心观点是:Rust的复杂度和C++的复杂度根本不是一回事——C++的问题在于反复为同一个问题造多个轮子,而Rust的新特性大多是解决彼此独立的新问题,不易牺牲整体一致性。他的结论很干脆:“让Rust更复杂,也就是让Rust更好。”
这场讨论里最出圈的一条回复提出,判断一个新特性是不是“甜蜜的负担”,可以从三个维度去看:
按这套标准,这条回复明确表示自己并不看好命名/默认参数、闭包里的use语法糖,但会非常支持显式尾调用和可变参数——理由是它们属于“广泛且解锁能力”的类型。
这套框架后来被多位评论者反复引用,成了这场讨论里最接近“共识”的分析工具。
最有分量的回复,来自ID为JoshTriplett的用户——他在个人标签里标注了rust、lang、libs、cargo,是Rust语言团队的成员。他特别说明,以下发言仅代表个人,不代表团队整体立场。
他给出的解释大致可以归纳为三类:
讨论过程中,还有网友甩出了一个佐证:根据2026年3月发布的Rust官方State of Rust年度调查结果,约有40%的受访者表达了与楼主类似的担忧,认为语言应该优先做“简化”而不是“加法”。
这也从侧面说明,这篇帖子戳中的并不是一个人的焦虑,而是一部分Rust用户群体真实存在的集体情绪。
不过也有老用户站出来“降噪”。一位自称从2014年开始写Rust,本人也是servo、rust、clippy等项目的贡献者表示,类似的“塌方警报”几乎每年都会响一次,但过去十年里从未真正发生过——原因是Rust的RFC流程本身就设有相当长的观察期和“复杂度预算”审查机制,大多数提案要经过数年讨论才可能落地,中途夭折的比例极高。
这场讨论最有意思的地方,或许不在于谁对谁错,而在于它精准地呈现了一个悖论:
一门语言越成功、用户越多,等待被解决的“长尾需求”就越多;而每一个长尾需求背后,都有一小撮开发者真心觉得“这个特性必须加,不然我没法用”。楼主在帖子里自己也承认这一点——他并不否认这些提案大多“确实有用”,只是担心把它们全部叠加起来,会不会在某个临界点上,让Rust从“精巧”滑向“臃肿”。
C++用了近三十年时间,才从"简洁的C升级版"变成今天动辄两千页标准、多套编译器实现互不兼容的庞然大物。Rust才十年历史,走到今天这一步是否也是必然,恐怕没人能给出确定答案。
但至少从这篇帖子的讨论质量来看,Rust社区对这个问题的警觉程度,本身可能就是它区别于前辈的地方。
*本文编译整理自Reddit r/rust板块讨论串《My concerns about the future of Rust》,原帖及评论区观点归属原作者所有。https://www.reddit.com/r/rust/comments/1wat0px/my_concerns_about_the_future_of_rust/ *
还在为写 Agent 框架频频死循环、上下文爆炸而束手无策?我的新专栏 《从0 开始构建 Agent Harness》 将带你:
扫描下方二维码,开启从 0 开始构建Agent Harness 的实战之旅。

还在为“复制粘贴喂AI”而烦恼?我的新专栏 《AI原生开发工作流实战》 将带你:
扫描下方二维码,开启你的AI原生开发之旅。

商务合作方式:撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。如有需求,请扫描下方公众号二维码,与我私信联系。

2026-09-11 06:00:00
本文永久链接 – https://tonybai.com/2026/09/11/go-simd-kills-cgo-turbopfor-avx512
大家好,我是Tony Bai。
一段跑了7年的C语言压缩库,被Go 1.27的实验性SIMD包干掉了,AI还顺手甩出了一个2倍加速的“外挂”。
【导读】:今年8月,Debian Code Search的作者Michael Stapelberg做成了一件憋了很多年的事——删掉项目里最后一行cgo代码。这背后的功臣,是Go 1.26引入、在Go 1.27上愈发成熟的实验性simd/archsimd包。他用纯Go重写了跑了7年的C语言TurboPFor压缩库,不仅性能反超,还借助AVX512的“位置汇编计数”黑科技把速度又翻了一倍——而这个关键优化,是AI编程助手Claude发现的。
【文章要点】
simd/archsimd 包,将服役 7 年的核心 TurboPFor 整数压缩库彻底重写为纯 Go,删除了项目中最后一行 cgo 代码;GF2P8AFFINEQB 和 VPOPCNTB 的“位置汇编计数(Positional Popcount)”正交变换优化,将单值处理指令数从 12 条暴降至 1.5 条,在追平 cgo 的基础上再次翻倍;
Debian Code Search(简称DCS)是一个搜索引擎,用来在整个Debian发行版的开源代码里做字面量或正则表达式检索。它的核心是一套倒排索引:从“词项”映射到“包含该词项的文档”,而文档通常用整数ID表示,所以索引本质上是海量的整数列表。
这些整数列表需要被极高效地压缩和解码,否则索引既存不下,查询也快不起来。DCS采用的是业界知名的TurboPFor整数压缩格式,多年来一直依赖C语言的powturbo/TurboPFor库,通过cgo接入Go项目。
问题是,DCS从一开始就被设计成一个纯Go项目,作者一直不喜欢代码库里混进C代码——但SIMD(单指令多数据)级别的性能,过去在Go里几乎无法企及。这个心结,一压就是7年。
在实验性simd/archsimd包出现之前,Go开发者如果想榨干CPU的向量指令性能,基本只有三个选择,而且每一个都不轻松:
bytes.IndexByte就是用手写汇编(含AVX2)实现的,但代码可读性和可维护性都很差;crypto/internal/fips140/sha256的AVX2实现就是这么来的。这比纯手写汇编“高级”一些,但本质上仍然是在跟汇编打交道;三条路各有各的坑:汇编难写难维护,代码生成器门槛高,cgo则意味着要接受一门“外来语言”长期驻留在纯Go项目里,交叉编译、构建流程都会因此变复杂。
Go 1.26(2026年2月发布)带来了转机——官方发布说明写道:
Go 1.26引入了实验性的
simd/archsimd包,可以通过在构建时设置环境变量GOEXPERIMENT=simd来启用。该包提供对特定架构SIMD操作的访问,目前支持amd64架构,提供128位、256位和512位的向量类型(如Int8x16、Float64x8),以及Int8x16.Add这样的操作。目前API尚未稳定。
到了Go 1.27,这套实验性能力已经足够扎实,可以支撑一个真正的生产项目去验证——这正是本文要讲的故事的技术底座。
下面这张图梳理了Go在SIMD这件事上走过的路:

在动手优化之前,作者先梳理了DCS对整数编解码的三种使用场景——局部索引(新软件包入库时的增量编码)、索引合并(把海量局部索引合并成少数几个大索引)、查询解码(用户搜索时高并发解码)。据此他设计了一套简洁的API:
package pforenc
type BlockEncoder struct {
// 复用的临时缓冲区放在这里
}
// EncodeBlock 把不超过256个uint32编码进dest(一个TurboPFor块)
func (*BlockEncoder) EncodeBlock(dest []byte, vals []uint32) []byte {}
// EncodeN 循环调用EncodeBlock
func (*BlockEncoder) EncodeN(dest []byte, vals []uint32) []byte {}
type StreamEncoder struct {
be BlockEncoder
vals [256]uint32
// 临时缓冲区
}
// 满了就需要调用EncodeBlock
func (*StreamEncoder) Add(val uint32) (full bool)
func (*StreamEncoder) EncodeBlock() []byte {
if se.n == 0 {
return nil // 多余的调用变成空操作
}
// …
}
最初的编码器实现简单到近乎“偷懒”:所有数值都按32位定长、小端序写入,每256个值加一个字节的块头:
func (be *BlockEncoder) EncodeBlock(dest []byte, vals []uint32) []byte {
const bitWidth = 32
dest = append(dest, bitWidth)
for _, val := range vals {
dest = binary.LittleEndian.AppendUint32(dest, val)
}
return dest
}
这当然是个压缩率极差的实现,但它是个可用的起点。接下来作者逐一实现了TurboPFor真正的几种块类型:bitpacking(按最小必要位宽定长压缩)、bitpacking with exceptions(用位图标记少量“超标”的异常值)、bitpacking with VB exceptions(用变长字节编码异常值,适合异常值很少的场景)、constant(整块只存一个值,适合全零或全相同的块)。
做完这一步,一个关键发现浮出水面:编码器真正的开销大头,是扫描输入值来决定用哪种块类型,而不是编码本身——这个发现,为后面那个“2倍加速彩蛋”埋下了伏笔。
此时,朴素版Go编码器的性能已经达到C版本的76%。作者原本觉得,到这一步其实就可以收工了。但他还是决定,看看到底能把Go推多远。
在正式动手优化前,作者花了不少篇幅讲“怎么量得准”,这一段对任何做性能优化的工程师都很有参考价值。
设置正确的微架构等级(GOAMD64)。Go从1.18开始支持通过GOAMD64指定编译目标的x86-64微架构等级:
| 等级 | 包含指令集 |
|---|---|
| v1(默认) | 所有x86-64处理器都支持的基线指令 |
| v2 | v1 + CMPXCHG16B、POPCNT、SSE4.2等 |
| v3 | v2 + AVX、AVX2、BMI1/2、LZCNT、FMA等 |
| v4 | v3 + AVX512F/BW/CD/DQ/VL |
作者建议2026年一般项目至少设置GOAMD64=v3,这样像bits.OnesCount8这类函数会被编译成POPCNT硬件指令而不是查表实现。DCS这个项目由于服务器和开发机都是较新的AMD Zen 4/Zen 5,直接用上了GOAMD64=v4(需要AVX512支持)。
基准测试与benchstat。作者用Go内置的testing包写基准测试,并用golang.org/x/perf/cmd/benchstat工具对比不同commit的性能变化,同时用taskset把测试进程锁定在固定核心上,避免核心迁移带来的噪声。
用perf看CPU硬件计数器。光看pprof知道“哪里慢”还不够,作者进一步用Linux的perf工具读取CPU硬件性能计数器(比如分支预测失败次数),来搞清楚“为什么慢”,这是Intel提出的“自顶向下分析法”(Top-down Analysis)的典型应用。
Profile-Guided Optimization(PGO,画像引导优化)是Go 1.21起正式可用的特性:先采集一份CPU性能画像,再喂给编译器,让它更激进地内联函数、做条件去虚化。
按理说PGO应该是稳赚不赔的优化,但作者开启PGO后,性能反而下降了13%!排查后发现,PGO会让编译器给热循环打上PCALIGNMAX(64,31)的对齐标记——按照AMD官方的Zen 5优化指南,把热循环对齐到64字节缓存行边界通常是好事。
但这次运气不好:对齐之后,一对被宏融合的CMPQ+JGE指令恰好落在了32字节边界上,而Go编译器为了修复Intel的SKX102勘误(这两条指令绝不能跨越或落在32字节边界),会插入额外的NOP指令来避让——这些额外指令拖慢了本就是“指令派发瓶颈”的循环。
这是一个很好的提醒:编译器优化并非单调递增,理解它的副作用同样重要。所幸后续的优化commit改变了代码结构,这个“不走运的排列”并未在整个优化过程中反复出现。
原教学版解码器每次需要临时缓冲区时都直接make()分配,这类由变量长度决定、无法在编译期确定大小的分配,会实实在在地走一次runtime.makeslice调用。
作者把这类临时缓冲区改为结构体里预先分配好的固定数组字段(复用而非每次新建),在DCS的debian-mix基准上把速度从773 Mval/s提升到858 Mval/s,提升约11%。这个改动同时也让基准测试更稳定,因为垃圾回收器被请出了热路径。
这是这一波优化里最有意思的一招。TurboPFor的bitpacking核心函数,其控制流实际上只取决于两件事:输入值的数量和要打包的位宽。如果这两者在编译期就已知,编译器就能把循环完全展开,生成几乎没有分支、只剩位运算和内存读写的“最优机器码”。
先把输入数量固定为32,手动展开循环;再借助Go泛型,用数组类型自带长度信息这一特性,把位宽也变成编译期常量:
type bitWidthT interface {
[1]byte | [2]byte | [3]byte | [4]byte | [5]byte |
[6]byte | [7]byte | [8]byte | [9]byte | [10]byte |
// … 一直到 [32]byte
[31]byte | [32]byte
}
func bitpack32Unrolled[T bitWidthT](dest []byte, vals *[32]uint32) {
var zero T
bitWidth := len(zero) // 编译期已知
dest = dest[: 4*bitWidth : 4*bitWidth] // 容量也编译期已知
mask := uint32(1<<bitWidth - 1)
var acc uint64
var have, pos int
// 循环体在这里手动展开,vals[0]..vals[31]逐一处理
acc |= uint64(vals[0]&mask) << have
have += bitWidth
if have >= 32 {
binary.LittleEndian.PutUint32(dest[pos:pos+4], uint32(acc))
pos += 4
acc >>= 32
have -= 32
}
// …
}
编译器会为每一种位宽实例化出一份独立的函数,生成的机器码几乎是无分支的、只由确定操作数的移位和位运算构成。效果非常明显——在处理256值以下的“余块”(remainder block)时,各类场景普遍取得40%到64%的加速:
vals=bitpacking-bw1 751.2 → 1120.5 Mval/s (+49.15%)
vals=bitpacking-bw2 716.8 → 1176.0 Mval/s (+64.07%)
vals=bitpacking-bw7 700.0 → 1078.5 Mval/s (+54.08%)
vals=debian-mix 559.5 → 783.8 Mval/s (+40.09%)
代价是二进制体积略有增大(.text段约增加20KB),作者认为这个代价完全值得。
即便不上真正的SIMD指令,只是把处理步长做大(比如用bits.OnesCount64一次数64位而不是逐位判断),也能带来提升。但要触及数量级的性能差距,还得靠向量指令。
simd/archsimd包需要考虑一个现实问题:不是所有CPU都支持AVX2/AVX512,程序需要在运行时做特性探测,并在旧CPU上优雅回退到标量实现。典型的三文件模式是这样的:
// constant_nosimd.go —— 不满足SIMD条件时的兜底实现
//go:build !goexperiment.simd || !amd64
package pfordec
func fillConstant(output []uint32, val uint32) {
fillConstantScalar(output, val)
}
// constant_amd64.go —— amd64架构下的SIMD实现,运行时探测AVX2
//go:build goexperiment.simd && amd64
package pfordec
import "simd/archsimd"
var hasAVX2 = archsimd.X86.AVX2()
func fillConstant(output []uint32, val uint32) {
if !hasAVX2 {
fillConstantScalar(output, val)
return
}
val8 := archsimd.BroadcastUint32x8(val)
i := 0
for ; i+8 <= len(output); i += 8 {
val8.StoreArray((*[8]uint32)(output[i : i+8]))
}
fillConstantScalar(output[i:], val) // 处理剩下不足8个的元素
}
当GOAMD64设置为v3或更高时,甚至可以直接把hasAVX2定为编译期常量true,省掉运行时判断的开销。DCS最终针对编码器和解码器的不同函数,分别用到了AVX2、AVX512,乃至AVX512+VBMI+GFNI+BITALG这些更细粒度的指令子集组合。
TurboPFor有一种专为SIMD设计的“垂直布局”(256 uint32 vertical layout),同时处理8个uint32小端值,充分利用AVX寄存器宽度。
常规bitpacking的位布局示意图:

256值垂直布局(SIMD bitpacking)的解码顺序示意图:

标量版本用8个uint64累加器,逐一处理8个值:
func bitunpack256v32(input []byte, dest []uint32, bitWidth int) (read int) {
mask := uint64(1)<<bitWidth - 1
var bits uint
var acc [8]uint64 // 累加器:当前位+剩余位
for op := 0; op < len(dest); {
if bits < uint(bitWidth) {
for i := range 8 {
acc[i] |= uint64(binary.LittleEndian.Uint32(input)) << bits
input = input[4:]
}
bits += 32
}
for i := range 8 {
dest[op] = uint32(acc[i] & mask)
op++
acc[i] >>= bitWidth
}
bits -= uint(bitWidth)
}
return len(dest)
}
SIMD版本同样处理8个值一组,但没有for i := range 8这层循环——8个值被“压”进了一个向量寄存器里并行处理。由于AVX2寄存器一次只能装8个uint32(装不下uint64),累加器被拆成rest8和cur8两个Uint32x8:
func bitunpack256v32(fullinput []byte, fulldest []uint32, bitWidth int) (read int) {
dest := fulldest[:256]
n := 32 * int(bitWidth)
input := fullinput[:n]
mask8 := archsimd.BroadcastUint32x8(uint32(1)<<bitWidth - 1)
bitWidth8 := archsimd.BroadcastUint32x8(uint32(bitWidth))
var bits uint
pos := 0
var rest8, cur8 archsimd.Uint32x8
for op := 0; op < 256; op += 8 {
if bits < uint(bitWidth) {
next := archsimd.LoadUint8x32(input[pos : pos+32]).ReshapeToUint32s()
pos += 32
cur8 = rest8.Or(next.ShiftAllLeft(uint64(bits)))
rest8 = next.ShiftAllRight(uint64(uint(bitWidth) - bits))
bits += 32
} else {
cur8 = rest8
rest8 = rest8.ShiftRight(bitWidth8)
}
cur8.And(mask8).Store(dest[op : op+8])
bits -= uint(bitWidth)
}
return n
}
这个版本benchmark下来,速度是标量版本的约3倍。再叠加前面提到的泛型位宽特化,让bitWidth也变成编译期常量,速度还能进一步提升。
编码器套上同样的SIMD打包和AVX512异常收集手法之后,性能已经大致追平了cgo版本。但作者说,接下来发生的事让他很惊讶——Claude Fable 5指出,编码器的“扫描”阶段还可以用一种叫“位置汇编计数”(Positional Popcount)的技巧再提速一倍。
回忆一下前文提到的发现:编码器真正的瓶颈不是编码本身,而是扫描全部输入值、统计出一份“在每个位宽下需要多少个异常值”的直方图:
type stats struct {
// cnt[n]:有多少个值的bits.Len32(val) > n
// 即位宽为n时,需要多少个异常值
cnt [32 + 24]uint32
}
func scan(output *stats, vals []uint32) {
for _, val := range vals {
for b := range bits.Len32(val) {
output.cnt[b]++
}
}
}
用3个示例值直观理解:23(二进制0000010111,需要5位)、5(二进制0000000101,需要3位)、666(二进制1010011010,需要10位)。它们各自的“smear mask”(把最高位的1“抹开”到所有低位)分别是0000011111、0000000111、1111111111。用POPCNT可以高效地数出“一行”里有多少个1,但这里需要的是数“一列”——即所有输入值在第b位上一共有多少个1,这就是Positional Popcount,标准POPCNT指令帮不上忙。
作者参考了三篇论文/文章(2019年Klarqvist等人的AVX512进位保留加法器方案、2024年Harold Aptroot基于GF2P8AFFINEQB指令的实现、2025年Clausecker等人的改进版),最终选用了GF2P8AFFINEQB路线。
下面这张图梳理了位置汇编计数的核心流程:

值得一提的是,GF2P8AFFINEQB这条指令并不冷门——它也是Go 2025年推出的“Green Tea垃圾回收器”里的“压轴主角”。用Go SIMD实现出来是这样的:
func scanSIMD(output *stats, vals []uint32) {
ones16 := archsimd.BroadcastUint32x16(^uint32(0))
shuffle := archsimd.LoadUint8x64Array(&scanShuffle)
units := archsimd.LoadUint8x64Array(&scanUnits)
var acc archsimd.Uint8x64
idx := 0
for ; idx+16 <= len(vals); idx += 16 {
v := archsimd.LoadUint32x16(vals[idx : idx+16])
// 把每个值替换成它的smear mask
smear := ones16.ShiftRight(v.LeadingZeros()).ReshapeToUint8s()
// 先转置字节,再转置比特
matrices := smear.Permute(shuffle).ReshapeToUint64s()
transposed := units.GaloisFieldAffineTransform(matrices, 0)
// 一次性对64字节做popcount并累加
acc = acc.Add(transposed.OnesCount())
}
sum := acc.GetLo().ExtendToUint16().Add(acc.GetHi().ExtendToUint16())
sum.GetLo().ExtendToUint32().Store(output.cnt[0:16])
sum.GetHi().ExtendToUint32().Store(output.cnt[16:32])
// 剩下不足16个值的标量兜底路径
for _, val := range vals[idx:] {
for b := range bits.Len32(val) {
output.cnt[b]++
}
}
}
原本一个快速版的标量扫描函数,处理每个值大约需要12条指令;用上这套SIMD正交变换后,降到了每个值约1.5条指令——提速约8倍,直接反映在整体编码性能上,就是那个额外的2倍加速。
下面这张图汇总了整个优化过程的性能演进路径:

作者很诚实地指出,虽然新的Go实现已经超过了DCS过去用的cgo版本,但如果把同样的AVX512核函数和位置汇编计数技巧“对等移植”回C版TurboPFor,Go目前的benchmark结果仍然慢了约1.4倍。差距主要来自五个方面:
GOAMD64=v3/v4这一级的微架构,而不能像clang那样精确到“AMD Zen 4”。例如Go编译器会在每条POPCNT前插入XORL CX,CX,这是为了规避Intel Sandy Bridge到Skylake时代的“伪输出依赖”问题,但在AMD Zen芯片上其实并不需要;POPCNTL; ADDQ; LEAQ),clang只需要2条(popcnt; lea)。这类差距是否值得修复,往往因场景而异。Go的SIMD支持,第一次让开发者能够不依赖cgo、不依赖手写汇编,就用上现代CPU里那部分强大的向量计算能力——对TurboPFor这类计算而言,这是数量级的提速。
作者也坦率地分享了AI编程助手在这个过程中的价值:他用Claude Code(搭配Opus 5和Fable 5)处理了大量繁琐的性能调优工作——阅读objdump反汇编输出的速度远超人类,能发现人很难注意到的模式和关联,遇到编译错误或运行时panic也不会烦躁,只要给出可衡量、可达成的目标,它可以不知疲倦地反复试验。
作者特别强调,他没有“vibe coding”整个项目,而是在AI给出优化建议后亲自审阅、理解、确认。
从CPU性能计数器看,最终版本的数值解码速度达到了每周期7条指令(IPC),而这颗CPU的理论上限是8 IPC——已经相当接近极限。
对Go生态而言,SIMD支持的到来意味着更多原本必须依赖C语言或汇编才能达成的高性能场景,现在可以用纯Go实现,同时保留Go在内存安全、可维护性和跨平台交叉编译上的一贯优势。正如作者在文末所说:这是Go一个非常受欢迎的补充能力。
参考资料
simd/archsimd的介绍:https://go.dev/doc/go1.26#simd还在为“复制粘贴喂AI”而烦恼?我的新专栏 《AI原生开发工作流实战》 将带你:
扫描下方二维码,开启你的AI原生开发之旅。

你的Go技能,是否也卡在了“熟练”到“精通”的瓶颈期?
继《Go语言第一课》后,我的《Go语言进阶课》终于在极客时间与大家见面了!
我的全新极客时间专栏 《Tony Bai·Go语言进阶课》就是为这样的你量身打造!30+讲硬核内容,带你夯实语法认知,提升设计思维,锻造工程实践能力,更有实战项目串讲。
目标只有一个:助你完成从“Go熟练工”到“Go专家”的蜕变! 现在就加入,让你的Go技能再上一个新台阶!

商务合作方式:撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。如有需求,请扫描下方公众号二维码,与我私信联系。

2026-09-10 07:00:00
本文永久链接 – https://tonybai.com/2026/09/10/d2-goes-nonprofit-tala-open-sourced
大家好,我是Tony Bai。
【导读】
D2,那个和Mermaid齐名、被很多工程师用来画架构图的文本转图表工具,这周连发两条重磅公告:母公司Terrastruct停止运营,创始人把D2整体捐赠给Hack Club,项目转为完全免费、非营利、开源;紧接着,原本要靠付费才能用的核心资产——自动布局算法TALA,也以和D2主项目相同的协议开源了。
更值得玩味的是,创始人顺带官宣:以后D2的代码贡献将全部交给AI来写,但明确表示“不做MCP、不做API、不做LLM插件”。一个开源项目在被砍掉商业化收入之后,是怎么想清楚“以后要做什么、坚决不做什么”的?
如果你写过软件架构图,大概率见过D2。它用一段纯文本描述节点和连线,编译成SVG,语法比Mermaid更贴近“画图”的直觉,社区一直把它称为Graphviz、Mermaid、PlantUML的有力挑战者。
就在这周,D2项目连发两篇博客,信息量都不小:
这两件事放在一起看,比单独看任何一件都更有意思。下面详细拆解。
【文章要点】

D2从商业开源核心到独立非营利项目,经历了如下几个关键事件和时间节点:
简单说:过去D2走的是典型的“开源核心(open-core)”路线——语言本体和编译器免费开源,靠闭源的可视化IDE“D2 Studio”和自研布局算法“TALA”两个付费产品养活团队。现在,养它的商业公司没了,项目本身没有跟着一起消失,而是被“捐”给了非营利组织,原来收费的TALA也一起开源了。
D2创始人在公告中说得很直白:项目诞生和发展至今,一直是一家营利性公司旗下的开源核心业务,靠闭源的D2 Studio和TALA养活开发。现在公司要关停,D2会作为独立的、完全开源的非营利项目继续存在,由Hack Club提供财务代管(fiscal sponsorship)。
创始人表示会继续维护和改进D2,但坦言自己的时间投入会比过去有限,这已经不是他的主业了;如果有人愿意成为维护者,高质量的维护和贡献甚至可能进入由捐赠资金支付的合同关系,他认为这对有开源经验的早期职业开发者会是个不错的机会。
这里有个关键背景信息值得补充:根据D2官方的Sponsor说明页面,D2目前是由The Hack Foundation(Hack Club背后的501(c)(3)非营利组织)提供财务代管,捐赠在美国可以抵税;创始人和另一位共同创始人都不会从这笔基金中拿到任何报酬,资金去向完全公开可查。

这套结构和很多“公司捐赠开源项目给基金会”的先例(比如一些项目转投Linux基金会、Apache基金会)逻辑类似,核心是把项目的存续和某一家公司的商业命运脱钩。创始人特别强调:赞助D2不会影响项目的路线图、治理方式或许可证,D2依然是MPL-2.0协议下的开源项目,开发全程在GitHub上公开进行。
公告里最引发讨论的一段,是创始人对D2下一阶段开发方式的定调:
这一阶段的D2,靠的是纯手写代码。这个时代结束了。下一阶段的D2将由AI驱动——我的所有代码贡献都会使用AI完成,也欢迎社区提交AI生成的贡献,我会用AI去审核AI提交的内容。唯一的例外是文字内容:所有博客文章和对外沟通,我都会亲自撰写,不使用AI。
他给出的理由是效率:过去十多年他几乎每天都在写代码,但过去一年里,虽然仍在和软件打交道,却一行代码都没有亲手写过。他认为AI能把过去因为工程人力有限而只能停留在设想阶段的功能真正做出来,比如等距渲染器、从其他画图语言迁移过来的转译器等。
值得肯定的是,创始人并没有回避这个决定的争议性。他在公告中直接引用了开源维护者Neil Alexander今年6月的一篇博客《Please stop flooding our projects with AI slop to furnish your CV》[4]。那篇文章描述了一个真实但让人不太舒服的现象:不少开源项目最近收到大量由AI代写、贡献者本人几乎没有实质投入的PR和漏洞报告,有人怀疑这背后是有人在“刷GitHub简历”,Neil Alexander本人后来直接关闭了三个这样的PR,不作评论。
D2创始人的态度是:我知道这样做有争议、有代价,我不是在向任何人推荐这么做,只是想坦诚地把自己的计划告诉依赖D2的社区。这种“先把丑话说在前面”的坦率,某种程度上也是这篇公告本身比较少见的地方。
有意思的是,创始人在拥抱AI提效的同时,反而给项目定了更严格的边界,公告里专门开了一节叫“Non-goals”(非目标):
.d2源文件,不需要再给D2专门做一个调教过的LLM对话界面。这份“三不做”清单,读起来更像是一份克制的产品哲学声明——在拥有更强AI生产力工具的同时,主动给自己设边界,避免把“能做”和“该做”混为一谈。

其中第三条格外值得展开。创始人的判断是:现在的大模型已经很擅长“在二维空间里把图画得差不多”,但在自动布局这件事上仍然不够确定、不够可控、不够精确,这个局限短期内很难被彻底解决——所以你仍然需要一份文本化的“图源代码”作为最终产物的底层依据,就像网页仍然需要HTML,而不是靠像素级手绘出来一样。换句话说,D2想成为Agent时代“文本生成图形”的标准底座,而不是被Agent绕过去的旧工具。
如果说“转型非营利”是D2这周公告里的第一个重磅消息,那TALA开源就是紧随其后的第二个——而且从产品逻辑上看,这一步几乎是必然的:D2既然已经不再靠商业化生存,那么继续把核心布局算法闭源就没有意义了。
TALA,全称Terrastruct’s AutoLayout Algorithm,是D2团队自研的一种新型自动布局算法,专门为软件架构图设计。和D2内置的另外两种布局引擎Dagre、ELK不同,TALA走的不是“有向无环图(DAG)逐层展开”的路线,而是一种正交布局(orthogonal layout)算法,视觉效果更接近人在白板上手绘架构图的样子。
TALA的设计目标是同时优化多个“美学”指标,包括对称性、节点间的中位距离、信息流向、同类节点的聚类程度等等,算法本身融合了多篇图绘制学术论文的思路(代码中有引用),再加上团队自己的原创技术。
这次开源之后,TALA使用和D2主项目相同的MPL-2.0协议,代码已经并入D2 v0.9.0,安装后只需在渲染时加上--layout=tala即可启用:
d2 --layout=tala architecture.d2 architecture.svg
不想安装的话,也可以直接在浏览器里体验——D2的官方在线Playground(play.d2lang.com)现在同样支持TALA布局,且完全在客户端本地运行,不经过任何服务器。

| 维度 | Dagre / ELK(DAG系) | TALA(正交系) |
|---|---|---|
| 布局风格 | 分层、单向延展,适合长链路流程图 | 更接近白板手绘的正交布局 |
| 稳定性 | 增删节点后局部变化,整体形态基本稳定 | 有随机性,同样输入相同种子结果一致,但增加一个节点后可能整体重排 |
| 坐标控制 | 一般由算法全权决定 | 支持部分或全部节点手动锁定top/left坐标,算法负责剩余部分与连线路由 |
| 适用场景 | 偏“流程”类的长链路架构图、DAG关系图 | 偏“系统全景”类、需要对称美观的软件架构图;尤其适合AI生成图形后交给算法排版和走线的场景 |
| 性能 | 大图渲染相对线性 | 图越大耗时增长越快(非线性),大规模图谱场景需注意 |
官方博客里给出的对比方式也很实在:不是挑几张“TALA赢麻了”的图来秀,而是从GitHub上随手抓了7个真实存在的公开D2项目文件,用完全相同的编译器、完全相同的源码,只切换布局引擎,把TALA、Dagre、ELK三种效果并排放出来,并且坦承“对某些图,你可能反而更喜欢非TALA的布局”。

上图是三种布局引擎横向对比示例(以“Fulcro RAD architecture”项目为例,展示同一份D2源码分别用TALA / Dagre / ELK渲染的效果)
除了美学效果,TALA这次开源博客里其实埋了一条对AI时代更重要的信息:它支持节点位置和尺寸的自定义,也就是可以给部分或全部节点直接指定坐标(top/left),算法只负责剩下节点的自动摆放以及所有连线的路由(routing)。
创始人特意提到,这个特性“特别适合Agent场景”——大模型在二维空间里摆放几个方块的位置这件事做得不错,但要同时兼顾走线不交叉、间距美观、整体布局协调,目前仍然是模型的弱项。TALA的思路相当于做了一次分工:坐标交给模型(或人)来定,走线和排版细节交给算法来兜底。
第三组示例则演示了“部分锁定 + 部分自动”的混合模式:比如“The Printing Room”这个例子里,4个CMYK印刷工位被固定在同一行、等间距排列,以保持印刷流程的机械对齐感,而进料器、摄像头、配准控制器、烘干设备等另外10个节点则完全交给TALA自动摆放。
官方博客难得地对自家算法的短板讲得很直接,值得原样记录:
这种“既秀成果又主动交代局限”的写法,其实也侧面印证了D2团队一贯的风格——不做过度营销,把选择权交给使用者。
抛开“又一个开源项目变动”的表面新闻属性,这次D2的操作组合,至少有三层信息值得留意:
第一,对普通使用者来说是实打实的利好。 原本需要付费才能用的IDE增强能力和高质量布局算法,现在都免费拿到手了,尤其是TALA的坐标锁定特性,对正在探索“用AI批量生成架构图”的团队会很有吸引力。
第二,它给“开源项目如何摆脱单一商业公司依赖”提供了一个具体案例。 不是项目直接停止维护,也不是简单换个东家卖掉,而是通过基金会财务代管的方式,把项目的存续和某一家公司的经营状况彻底解绑,资金去向公开透明可查,这种治理结构对其他面临类似困境的开源项目有参考价值。
第三,创始人对“AI驱动开发”的坦率表态,本身就是一次公开的实践样本。 他没有回避AI贡献可能带来的质量风险和社区信任问题,反而主动引用了争议文章、承认这样做有争议,同时用“三不做”给项目划了清晰边界——这种“拥抱效率、但拒绝盲目扩张”的姿态,或许比技术细节本身更值得同行讨论。
D2这次的两连发公告,信息密度其实很高:一家公司关停、一个项目转型非营利、一套曾经的商业资产开源、一份关于AI驱动开发的坦率宣言、外加一份克制的产品边界声明,全部发生在同一周。对已经在用D2的团队来说,最直接的动作是升级到v0.9.0,把--layout=tala跑一遍试试效果;对关注开源治理和AI原生开发范式的朋友来说,这或许是一个值得持续跟踪的样本项目。
如果你也认可D2这种“非营利+完全开源”的路线,可以通过官方渠道进行捐赠,资金用途公开可查:https://hcb.hackclub.com/d2 。
参考资料
还在为写 Agent 框架频频死循环、上下文爆炸而束手无策?我的新专栏 《从0 开始构建 Agent Harness》 将带你:
扫描下方二维码,开启从 0 开始构建Agent Harness 的实战之旅。

还在为“复制粘贴喂AI”而烦恼?我的新专栏 《AI原生开发工作流实战》 将带你:
扫描下方二维码,开启你的AI原生开发之旅。

商务合作方式:撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。如有需求,请扫描下方公众号二维码,与我私信联系。

2026-09-09 06:00:00
本文永久链接 – https://tonybai.com/2026/09/09/why-harness-matters-more-than-model-yc-paper-club
大家好,我是Tony Bai。
【导读】
一个月前,Reddit 上有人吐槽:“这种 prompt engineering 不该出现在顶会上。” YC Paper Club 用一整晚的分享正面回应了这句质疑:同一份 Claude Opus 权重文件,裸跑 ARC-AGI-3 只有 30% 正确率,套上合适的 Harness 之后能冲到 95%,NVIDIA 的 AVO 甚至跑到了 100%。模型的智商在原地踏步,真正决定 Agent 天花板的,是那层长期被低估的“脚手架”。
【文章要点】

YC 每隔一段时间会办一场叫 Paper Club 的小型分享会,专门讨论一篇或几篇值得细读的论文。这一期的主题很直接——Harness,也就是套在大模型外面的那层运行时:系统提示词、工具调用、记忆管理、循环控制、权限系统……几乎所有让模型从“一个会聊天的黑盒”变成“能干活的智能体”的工程,都发生在这一层。
主持人开场就抛出了两条来自 Reddit 的评论:一条说这种“prompt engineering”不配出现在顶会上;另一条说“上下文工程根本不是研究问题”。Harness 长期被视为学术意义上的“边角料”——直到最近的数据打了所有人的脸。
METER 那张经典的“模型能持续自主工作多久”曲线一直在指数增长,但推动这条曲线上扬的,很大一部分并不是模型智商本身的提升,而是 Harness 的迭代。主持人举了一个更具体的例子:在 ARC-AGI 上,同一份 Claude Opus 权重,裸跑只能拿到 30% 的成绩,套上合适的 Harness 之后可以冲到 95%,而 NVIDIA 的 AVO 甚至跑到了 100%。这中间的 65 个百分点,模型权重文件一字未变。
Prime Intellect 研究员 Seth(Prime Agent 的作者)在分享里给出了一个很干净的定义:如果只看权重文件本身,LLM 其实只是一个序列处理器——输入一串 token,输出一串 token,用来预测下一步该做什么。而 Harness,是加在模型和真实世界之间的那一层,负责提供持久化状态、工具和算力。
换句话说,模型负责“思考”,Harness 负责让这个思考落地:读写文件、调用 API、维护记忆、协调多个子智能体、决定什么时候该继续干、什么时候该停下来。同一个模型套上不同的 Harness,表现可以有天壤之别——这正是这场分享想论证的核心命题。
主持人花了一个周末重读了从 Self-Refine、Reflexion、Voyager 到 Toolformer 的经典文献,压缩出一条五分钟版本的演化脉络。这条脉络不是严格按时间排列的,而是按“能力维度”组织的——每一步都在往 Harness 里加一种新的表达能力。

几个关键节点值得展开说一下:
skills.md,需要时检索调用——这基本就是今天“技能(Skill)”概念的原型。这一整套演化,主持人把它统称为 Harness V1:静态时代——无论工具、技能、子智能体列表配置得多复杂,Harness 本身(也就是这套代码逻辑)在运行过程中是不变的,改变的只是往上叠加的功能。下面是一个典型的 V1 循环架构:

最近半年,事情开始变得“更 trippy”——不是模型在学习,而是 Harness 本身开始学习。这里有三条代表性路线:
正是这一批工作,把 Harness 从“人工设计的脚手架”变成了“会自我改写的系统”,也是当晚三位主讲人分享的三个项目——Prime Agent、Open Jarvis、QM——共同的技术底色。
Prime Intellect 的 Seth 分享的 Prime Agent,被定义为“一个自我进化的 RLM Harness”。他的核心方法论是:把记忆按访问速度和持久性分成三层,类比 CPU 的缓存体系。

在这套结构里,Prime Agent 把子智能体做成了“持久化子会话”:主会话可以随时派生新的子智能体去执行任务,任务完成后子智能体并不会消失,而是进入“闲置”状态,仍然驻留在内存里,需要时可以直接把消息发回去继续工作,不必重建上下文——这解决了多智能体协作中最常见的“上下文丢失”问题。
实测数据上,团队在 ARC-AGI-3 上的调优过程颇为坎坷也颇为真实:第一轮直接套用一个社区系统提示词,跑出了 99.9% 的“惊人成绩”——结果一看日志,是“作弊”了(沙盒没做好隔离);重新搭好沙盒后,GPT-5 系列跑出 78%;换用其他模型也各有 70~80% 区间的成绩。团队还特意强调了一个容易被忽视的问题:很多评测在比较模型时,实际花费的预算(时间、token)并不对等,这会掩盖真实的性能差异,所以他们更看重“长程性能的实际拐点在哪里”,而不是单点分数。
除了 ARC-AGI,团队还做了一次相当极限的长程实验:用 8 台 H200 节点跑了 7 天,累计调度 633 个智能体、产出 2300 万 token,在一个类似“自动化工厂搭建”的任务里持续做技术树推进。有意思的是,直到实验结束,智能体依然没有陷入停滞,还在持续取得技术进展——这被主讲人半开玩笑地称为“我们自己的《Gemini Plays Pokémon》”。
Seth 给出的经验总结是:想把 Harness 做好,重点关注智能体化的上下文管理(分层记忆、压缩、精炼)、群体协作(Swarm)与 RLM 的深度使用,以及用标准化的评测方式衡量长程性能,而不是只看单点分数。
斯坦福团队分享的 Open Jarvis,出发点是一个很现实的问题:目前的个人 AI 几乎全部依赖云端大模型,代价是高昂的 API 费用、隐私风险,以及“你在租智能,而不是拥有智能”。他们想验证:能不能把模型推理、智能体执行、记忆与学习这套核心栈完全放到本地设备上跑,同时性能不明显掉队?
团队把任何一套个人 AI Harness 拆解成五个最基础的原语:
他们做的一个巧妙设计是:用云端大模型去自动优化本地小模型的 Harness 配置——诊断问题、提出改进方案、生成更优配置,但推理阶段完全不产生云端调用费用。
结果显示,经过这种优化的本地配置,相比“开箱即用”的本地部署有显著提升,在部分个人任务、编程、智能体任务上已经能和云端模型打平;即便仍有不少任务本地模型力有不足,这个差距正在按月收窄。
团队给出的关键数字是:成本最多可降低约 800 倍,同时延迟也显著下降。而且无论选用哪家云端模型来做优化器(Opus、GPT、Gemini、Kimi、GLM 等),都能带来收益——说明“用云端智能优化本地 Harness”这条路径本身具有一定的通用性。
如果说前两个项目更偏研究性质,YC 内部团队分享的 QM 项目则更像一份“血泪史”。QM 是 YC 自研的开源 Agent Harness,目标是让每个员工都拥有一个可深度定制、随时可在 Slack 或网页里调用的助手。
它的演进经历了四个阶段:

其中最关键的设计转折发生在 Hermes 舰队阶段之后:团队发现,把智能体的“大脑”完全绑定在某一台沙盒电脑里,看似强大,实则脆弱——一旦沙盒数量上来,管理成本会失控,而且所有会话记录都被“锁”在那台机器里,无法被系统整体利用。
QM 的解法是把所有对话与状态统一卸载到 Postgres,再暴露给 Agent 本身去调用,而沙盒不再是 Agent 的“家”,而是它按需取用的一种资源——需要重型开发环境就申请强力沙盒,任务简单就用轻量沙盒,这个决策权也被下放给了 Agent 自己,而不是写死在 Harness 逻辑里。
分享中还提到几个非常“接地气”的工程教训,很值得国内做 Agent 产品的团队参考:
把 Prime Agent、Open Jarvis、QM 放在一起看,几条设计原则反复出现:
这场分享传递的核心信息其实很朴素:过去几年里,模型的智商曲线(用主持人的话说,接近“IQ”维度)确实在稳步上升,但真正把这份智商转化为可用生产力的,是 Harness 这层长期被视为“配角”的工程。
ARC-AGI 上从 30% 到 95%、95% 到 100% 的跃迁,靠的不是换了更聪明的模型,而是给同一个模型换了一副更懂得怎么用它的“身体”。
当模型能力的边际提升开始放缓,Harness 会不会成为下一个真正的竞争高地?至少从这场分享的三个项目来看,答案已经相当明确。
本文根据 YC Paper Club 分享《Why The Harness Matters More Than The Model》整理编译,视频原文参见 YouTube(https://www.youtube.com/watch?v=n9xKblqyQ28)。
还在为写 Agent 框架频频死循环、上下文爆炸而束手无策?我的新专栏 《从0 开始构建 Agent Harness》 将带你:
扫描下方二维码,开启从 0 开始构建Agent Harness 的实战之旅。

还在为“复制粘贴喂AI”而烦恼?我的新专栏 《AI原生开发工作流实战》 将带你:
扫描下方二维码,开启你的AI原生开发之旅。

商务合作方式:撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。如有需求,请扫描下方公众号二维码,与我私信联系。

2026-09-08 06:00:00
本文永久链接 – https://tonybai.com/2026/09/08/go-127-http2-move-into-std
大家好,我是Tony Bai。
【导读】
2026 年 8 月,Go 1.27 正式发布。在一堆“泛型方法”、“JSON v2”、“后量子加密”这些吸引眼球的新特性背后,藏着一件低调却意义重大的工程大事:跑了整整两年、拆成 11 个子任务的跟踪计划 #67810 终于收官——HTTP/2 的“官方实现”从外部仓库 golang.org/x/net/http2 正式搬进了标准库 net/http/internal/http2。这背后,是 Go 核心团队一次教科书级别的“大型遗留重构”操作。
【文章要点】
golang.org/x/net/http2 这个外部模块里,标准库只是通过一个叫 bundle 的打包工具把它“塞”进来,生成一份谁都不敢手改的 h2_bundle.go。net/http 要兼容老版 x/net、用户想调个 HTTP/2 参数还得单独 import 一个包。net/http/internal/http2 成为唯一“真源”,golang.org/x/net/http2 反过来“包装”标准库实现,角色彻底反转。x/net。
如果你写过 Go 的网络服务,大概率享受过这样的“隐形福利”:你只需要 http.ListenAndServe,Go 就自动帮你把 HTTP/2 安排得明明白白,TLS 握手一谈妥,协议自动升级,你几乎感觉不到它的存在。
但很少有人知道,这套“自动挡”背后的真实实现,长期以来根本不在标准库仓库里,而是住在另一个独立维护的模块——golang.org/x/net/http2。
标准库要用它,得先经过一道特殊工序:用一个叫 bundle 的代码生成工具,把整个 http2 包“打包压平”成一份巨大的单文件 h2_bundle.go,塞进 net/http 包里一起编译。这么绕一圈的原因很简单:x/net/http2 本身依赖 net/http,如果标准库直接反向 import 它,就会形成导入环——Go 编译器是不允许这种循环依赖的。
于是我们得到了一个略显魔幻的架构:

这套“寄养架构”跑了整整十年(从 Go 1.6 到 Go 1.26),也确实撑住了 Go 生态最重要的网络协议升级。但代价也在慢慢累积。
2023 年 6 月,Go 团队核心成员 neild 在官方 Discussion 区发起了一次提案(#60746),把这些年攒下的问题一次性摊开:
x/net,再走一套复杂流程“倒灌”回 net/http,backport 过程繁琐易错。net/http 的新版本还得兼容旧版本的 x/net,两边版本矩阵越滚越大。import golang.org/x/net/http2,而且一旦这么做,标准库内置的实现会被整个替换掉——配置和“用哪套实现”这两件事被死死绑在了一起。一年后,也就是 2024 年 6 月,这份提案正式落地为跟踪 Issue #67810,标题干脆利落:net/http: move HTTP/2 into std(把 HTTP/2 挪进标准库)。目标也说得很直白:让标准库仓库成为 HTTP/2 实现的唯一“真源”(source of truth),并最终让 golang.org/x/net/http2 走向废弃。
这个项目最值得学习的地方,其实不是技术方案本身,而是工程管理方式。Go 团队没有搞“一次性大爆炸式重写”,而是把整个迁移拆解成 11 个可以独立设计、独立评审、独立合入的子任务,每一个都有自己的编号、自己的提案讨论、自己的验收标准:

拆解逻辑其实很清楚,可以归纳为三条主线:
第一条主线:先把配置能力“暴露”到标准库。 这是最先动手、也最先让普通用户受益的部分。#67813(HTTP/2 配置 API)和 #67814(协议版本选择 API)在 Go 1.24 就已经落地:标准库新增了 http.Protocols 类型和 http.HTTP2Config 结构体,用户第一次可以不引入任何外部包,就直接在 http.Server 或 http.Transport 上配置 HTTP/2 的行为。#67816 则顺带解决了一个长期为人诟病的问题——原生支持未加密的 HTTP/2(也就是常说的 h2c),此前这必须依赖 x/net/http2/h2c 这个“补丁包”才能实现。
第二条主线:把真正的协议实现代码“物理搬家”。 这是硬骨头,涉及把 x/net/http2 里成千上万行的 frame 解析、流控、多路复用、写调度器逻辑,原样迁移到新建的内部包 net/http/internal/http2,同时删掉大量只为兼容独立模块而存在的历史包袱(比如那些专为导出测试用的 ExportXxx 函数、ServeConnOpts.UpgradeRequest 这类过渡期 API)。从评论区可以看到,仅“初始导入”(CL 751300)之后,团队还花了大量精力清理测试代码、让 net/http/internal/http2 直接复用 net/http 自己的 Transport 和 Server 做测试,而不是自成一套测试基础设施。
第三条主线:把旧包“体面地”退休。 #67819、#77695、#78064 这三个 Issue 是一脉相承的关系——最早提议把 frame 操作单独拆包,后来评估后发现不如直接提议废弃整个 ClientConnPool,最终演变成一个更彻底的方案 #78064:直接废弃 x/net/http2 里的 Transport、Server、ConfigureServer、ConfigureTransport 等一整套 API。逻辑很简单——这些能力标准库现在都有了,没必要留两份。最后一步 #78508,则让 x/net/http2 摇身一变成为 net/http 的一层“包装壳”,专门服务那些暂时还没升级、依旧直接 import 老包的存量用户。
整条主线走完,正好呼应了 issue 里那句朴素的目标:“先让 x/net/http2 里每一个非废弃功能,在别处都能找到(配置项进 net/http,部分功能进新包,部分直接废弃)”。
2026 年 8 月,Go 1.27 正式发布,这场持续两年的迁移画上了句号。架构关系发生了根本性反转:

用官方仓库 x/net/http2 自己 README 里的话说得非常直白:
从 Go 1.27 开始,真源已经转移到标准库包
net/http/internal/http2。所有新特性开发都应该发生在那个包里,x/net只会继续得到关键 bug 修复和安全补丁的回合并。
也就是说,golang.org/x/net/http2 并没有立刻消失,而是转型成了两种实现的"外壳":对 Go 1.27 以下版本,它还是原班人马的老实现;对 Go 1.27 及以上版本,它默认变成一层薄壳,内部直接调用 net/http(如果你还想用回老实现,可以加上 http2legacy 编译标签)。
用一条时间线回顾整个过程会更直观:

先说结论:大部分人什么都不用改。这次迁移最重要的设计原则之一,就是完全遵守 Go 1 兼容性承诺——你的老代码、老依赖,不需要任何改动就能继续在 Go 1.27 上跑,无论你是否使用了 HTTP/2。
但有几件事,从现在开始会变得简单很多:
1. 配置 HTTP/2 参数不再需要额外依赖。 以前想调整最大并发流数量,得这样写:
import "golang.org/x/net/http2"
http2.ConfigureServer(srv, &http2.Server{
MaxConcurrentStreams: 250,
})
现在直接用标准库字段就行:
srv := &http.Server{
Addr: ":8080",
HTTP2: &http.HTTP2Config{
MaxConcurrentStreams: 250,
},
}
2. 启用未加密的 HTTP/2(h2c)不再需要 x/net/http2/h2c 这个补丁包。
srv := &http.Server{Addr: ":8080", Handler: mux}
srv.Protocols = new(http.Protocols)
srv.Protocols.SetHTTP1(true)
srv.Protocols.SetUnencryptedHTTP2(true)
srv.ListenAndServe()
3. 安全补丁的响应速度会明显变快。 过去一个 HTTP/2 层面的安全漏洞,需要先在 x/net 修复、发版,再走一遍搬运流程进入标准库的下一个发布周期;现在真源就在标准库仓库里,修复和发布可以在同一个release cycle里原子完成。
4. 如果你的项目还直接 import golang.org/x/net/http2 用它的 Transport 或 Server,目前不会立刻报错,但官方已经明确表态要废弃这批 API(见 #78064),建议尽早评估迁移到 net/http 原生能力的可行性。
抛开 HTTP/2 本身的技术细节,这个持续两年的项目更像是一份“如何优雅地重构一个所有人都在用、谁都不敢乱动的核心组件”的操作手册:先公开摊牌讲清楚“为什么要改”(Discussion 提案),再拆成一串可以独立评审、独立合入、互不阻塞的小任务(11 个子 Issue),过程中每一次代码提交都关联回同一个跟踪入口,外部社区随时能看到整体进度。
issue 最后一条评论,只有两个字:“And done.”——干净利落,正如整个工程本身。
对于每天都要维护“改不动又不敢不改”的存量系统的工程师来说,这或许比 HTTP/2 本身更值得收藏。
参考链接
还在为写 Agent 框架频频死循环、上下文爆炸而束手无策?我的新专栏 《从0 开始构建 Agent Harness》 将带你:
扫描下方二维码,开启从 0 开始构建Agent Harness 的实战之旅。

你的Go技能,是否也卡在了“熟练”到“精通”的瓶颈期?
继《Go语言第一课》后,我的《Go语言进阶课》终于在极客时间与大家见面了!
我的全新极客时间专栏 《Tony Bai·Go语言进阶课》就是为这样的你量身打造!30+讲硬核内容,带你夯实语法认知,提升设计思维,锻造工程实践能力,更有实战项目串讲。
目标只有一个:助你完成从“Go熟练工”到“Go专家”的蜕变! 现在就加入,让你的Go技能再上一个新台阶!

商务合作方式:撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。如有需求,请扫描下方公众号二维码,与我私信联系。

2026-09-07 07:00:00
本文永久链接 – https://tonybai.com/2026/09/07/the-physical-ai-gold-rush
大家好,我是Tony Bai。
【导读】
当整个行业还在卷各类“ChatGPT 套壳”和聊天机器人时,科技界的底层范式转移已经悄然走向现实物理世界。海外技术博主 Rich Odin 撰文指出:过去二十年软件吞噬了世界,而以具身智能为核心的“物理 AI”即将消化它。从僵硬死板的 C++ 运动学控制,跨越到端到端直接输出电机指令的 VLA(视觉-语言-动作)大模型,机器人的研发逻辑彻底被重构为了“下一个 Token 预测”。在这篇极具前瞻性的万字长文中,作者不仅用生动的真实翻车案例剖析了技术现状,更梳理出软件工程师与创业者无需造重硬件即可切入的四大高利润软件变现路径。
【文章要点 (TL;DR)】

过去二十年,以移动互联网和云端 SaaS 为代表的“纯数字软件”重塑了全球商业版图;而过去两年,大语言模型与生成式 AI 的爆发更将数字经济推向了巅峰。然而,当大部分创业者还在卷各类“ChatGPT 套壳”应用时,科技行业底层的范式转移已经悄然启动——AI 正从云端走向现实物理空间。
这篇来自海外技术博主 Rich Odin 的深度长文《物理 AI 淘金热:为何机器人是下一个数万亿美元级的超级风口》,用犀利且极具洞察力的视角指出了未来十年的核心趋势:“软件吞噬了过去二十年的世界,而物理 AI(Physical AI)即将消化它。” 文章不仅列举了机器人领域近期令人哭笑不得的真实翻车案例,剖析了以 VLA(视觉-语言-动作)大模型为核心的技术栈代际变革,还梳理出目前机器人产业链中利润率最高的四大纯软件/中间件商业变现模式。
无论你是软件工程师、AI 研究者、投资人,还是寻找下一个时代机遇的创业者,这篇干货满满的文章都非常值得精读。以下为全文完整译文:
“软件吞噬了过去二十年的世界。而物理 AI(Physical AI)即将消化它。”
多年来,机器人技术一直是风险投资的“坟场”。
那时你看到的,尽是过度工程化的硬件、脆弱易崩的 C++ 控制脚本,以及一个纸箱倾斜 3 度就会彻底死机卡住的机器。
但如今,我们正真切地经历着科技史上最大的一次底层结构性范式转移:
如果你觉得上一轮 AI 纯软件繁荣创造的财富已经足够疯狂,不妨等等看当物理机器开始在现实世界中大规模替代并执行经济生产劳动时,会发生什么。
以下是关于机器人市场为何正在迎来大爆发的不加修饰的真相、目前正在发生的真实魔幻案例,以及如何将其商业变现的实战打法指南。
在深入探讨枯燥的数字之前,我们先来看看现实物理世界的 AI 正在以多快的速度“发疯失控”:
五角大楼的“幽灵机器狗”乌龙事件: 2024 年底,一台配备了全新 AI 视觉系统的自主四足机器狗投入实地测试。
在一次封闭式演练中,由于动态阴影的光线干扰,该模型将一个普通的垃圾桶误判为具有敌意的威胁目标,随后果断执行了精准的动能冲撞打击……狠狠撞翻了一个塑料分类回收桶。
在不到 36 个月的时间里,我们竟然就已经从“机器人还走不稳路”,快进到了“机器人因存在主义危机而在高速状态下发神经”。
无限死循环的咖啡师: 2025 年初,日本东京某试验点的一台咖啡制作机械臂遭遇了未经处理的边缘死循环故障。
由于某个传感器未能成功上报“纸杯未就位”的状态,这台机械臂在空无一物的托盘上方整整连续倾倒了 6 个小时、共计 420 杯拿铁。
损失有多大?浪费了价值 3,000 美元的燕麦奶。但这起事故的视频在 X(推特)上狂揽了超过 4000 万次播放。
从仿真到现实的“跨次元瞬移”(Sim-to-Real): 此时此刻,各大具身人形机器人公司正让模型完全在超写实的物理仿真世界中(例如 NVIDIA Isaac Sim)进行强化学习训练。
一台人形机器人在真实脚掌踏上现实世界的地板之前,仅仅在一个下午之内,就能在仿真环境里经历相当于 50 年的物理试错与摸爬滚打。

为什么是现在?
因为我们终于彻底放弃了编写僵化死板的逻辑规则,转而开始将机器人控制视为一个“下一个 Token 预测问题”(Next-Token Prediction Problem)。
在过去,如果你想让机械臂抓取一个苹果,你必须手写成千上万行运动学逆解(IK)、轨迹规划数学公式以及硬编码的传统计算机视觉算法。
下面是一套高度精简的 Python 架构示例,展示了在边缘端设备(甚至在原型开发阶段直接在你的 Mac 上)运行现代 VLA 感知与控制的核心逻辑:
# 现代 AI 机器人控制技术栈 v2.4
import torch
import numpy as np
force_sensors: np.ndarray
class VisionLanguageActionModel(torch.nn.Module):
def __init__(self, model_checkpoint: str):
super().__init__()
# 加载多模态物理具身基座模型(如 RT-2 / OpenVLA 架构风格)
self.vla_backbone = torch.hub.load('robotics/vla_core', 'vla_base', pretrained=True)
def predict_action(self, image: np.ndarray, prompt: str) -> np.ndarray:
"""
接收高分辨率摄像头画面帧 + 自然语言交互指令。
直接预测并输出连续的 7 自由度(7-DOF)空间速度轨迹向量。
"""
tensor_img = torch.from_numpy(image).permute(2, 0, 1).unsqueeze(0).float()
action_logits = self.vla_backbone(tensor_img, prompt)
return action_logits.detach().cpu().numpy()
class RealtimeSafetyController:
def __init__(self, frequency_hz: int = 1000):
self.freq = frequency_hz
self.max_force_limit_newtons = 45.0
def validate_trajectory(self, planned_action: np.ndarray, current_state: RobotState) -> np.ndarray:
# 确定性高频安全校验闭环(1000Hz 超高频循环)
if np.any(current_state.force_sensors > self.max_force_limit_newtons):
print("[紧急警报] 接触受力超过阈值!正在触发柔顺阻尼缓冲保护。")
return planned_action * 0.1 # 瞬间将动作速度降低 90%
return planned_action
# 初始化运行入口
if __name__ == "__main__":
brain = VisionLanguageActionModel(model_checkpoint="vla-7b-embodied")
safety_layer = RealtimeSafetyController(frequency_hz=1000)
print(">> 硬件无关的 AI 大脑初始化成功。")
print(">> 准备就绪,开始低延迟流式输出电机动作指令。")
从头造物理硬件需要天量沉重的基础资本开支(CapEx)。
而真正聪明的资本和创业者正在涌向:
> 核心软件系统
> 数据中间件生态
> 垂直整合应用层
以下是目前机器人产业中利润率最高的 4 种商业模式:
硬件原始设备制造商(如宇树 Unitree、优傲 Universal Robots、库卡 KUKA)在制造高性能电机、高精减速箱和轻量化铝合金机架方面极具优势,但他们做出来的配套软件交互体验往往非常糟糕。
具身 AI 模型要想在严苛的企业级场景中达到 99.99% 的工业级可靠性,需要数以万亿计的现实物理世界数据点(实时遥测数据、高频触觉受力反馈、动态偶发边缘工况)。
千万不要好高骛远去尝试解决所谓的“通用家庭保姆家务”。找到极其具体、环境脏乱、危险系数高或员工流失率极高的 B 端企业痛点:
当 AI 机器人在现实中遭遇从未见过的罕见异常(例如地图中未标定的高空坠落物)时,它绝对不应该直接死机或失控,而是应该立即发起一个只需 5 秒钟的人工介入接管请求。
移动互联网 App 时代催生了一大批纯软件独角兽公司。
而物理 AI(Physical AI)时代,必将孕育出人类历史上首批规模达到数万亿美元的超级自动化企业集群。
想要在这个历史级的浪潮中分得一杯羹,你并不需要拥有机械工程专业的博士学位。你只需要吃透并掌握:
→ 别再继续折腾无聊的 ChatGPT 套壳应用了。
→ 去为现实物理世界构建真正改变世界的软件吧。
感谢阅读!
原文链接:https://x.com/rich_odinn/status/2088006989062303788
还在为写 Agent 框架频频死循环、上下文爆炸而束手无策?我的新专栏 《从0 开始构建 Agent Harness》 将带你:
扫描下方二维码,开启从 0 开始构建Agent Harness 的实战之旅。

还在为“复制粘贴喂AI”而烦恼?我的新专栏 《AI原生开发工作流实战》 将带你:
扫描下方二维码,开启你的AI原生开发之旅。

商务合作方式:撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。如有需求,请扫描下方公众号二维码,与我私信联系。
