阅读视图

发现新文章,点击刷新页面。
🔲 ☆

谷歌一篇论文砸崩内存巨头?不懂“显存墙”,怎么做 AI 时代的工程师!

本文永久链接 – https://tonybai.com/2026/03/28/ai-engineer-gpu-introduction-course

大家好,我是Tony Bai。

就在最近,科技界发生了一件极其戏剧性的事情。本周三美股开盘,全球存储产业巨头——美光、西部数据、希捷的股价遭遇了“黑色时刻”,普遍明显下跌(3%~6%)。

引发这场资本市场大地震的,不是什么贸易战,也不是财报暴雷,而仅仅是谷歌(Google Research)发布的一篇技术论文:《TurboQuant: Redefining AI efficiency with extreme compression

这篇论文宣称,他们发明了一种极端的压缩算法,能在几乎零损耗的情况下,将大模型推理时的 KV 缓存(KV Cache)暴降 6 倍,并让注意力机制的计算速度狂飙 8 倍

很多传统的后端程序员看到这条新闻,可能一头雾水:

  • 什么是 KV Cache?
  • 为什么压缩了一个叫 KV Cache 的东西,就能让卖物理内存芯片的巨头们吓得半死?

在这些雾水和疑惑背后,隐藏着 AI 大模型时代最核心、也最残酷的技术真相:内存墙(Memory Wall)

AI 时代的底色:算力过剩,访存为王

在传统的软件开发中,我们习惯了用 CPU 的思维去思考性能。我们认为程序跑得慢,是因为“计算太复杂”,我们需要更强的算力(更快的 CPU 频率)。

但在大语言模型(LLM)的世界里,逻辑变了。

大模型在生成文本时,是逐字生成(自回归)的。为了不每次都把前面说过的话重新计算一遍,模型会把之前所有上下文的内部特征(Key 和 Value 矩阵)全部保存在显存里。这份庞大的“运行记忆”,就是 KV Cache

随着上下文越来越长(比如从 4K 飙升到 128K 甚至百万级),这份 KV Cache 会像滚雪球一样膨胀。

这就是为什么业界说:KV Cache 是大模型推理名副其实的“吞金兽”。

更要命的是,每次生成一个新的字,GPU 都必须把这份庞大的 KV Cache 从显存(HBM)完整地搬运到计算核心(SRAM)里过一遍。

这就好比你有一个世界上切菜最快的厨师(GPU 算力),但他每次切一片肉,都要跑到 10 公里外的仓库(显存)去取。厨师的手速再快也没有用,整体速度完全被运货卡车的速度(显存带宽)锁死了。

这就是困扰所有 AI 工程师的 “内存墙”。也是为什么各大公司疯狂抢购高显存、高带宽的 H100 显卡的原因。

而谷歌的 TurboQuant 之所以引发地震,正是因为它通过极致的数学算法(极坐标变换 + 1-bit 残差误差校验),直接在软件层面把搬运的数据量压缩了 6 倍!这意味着,同样的硬件,现在能跑更长的上下文、支持更高的并发。存储巨头们能不慌吗?

为什么后端工程师必须懂 GPU?

你可以说:“我只是个调 OpenAI 兼容API 的后端工程师,硬件底层关我什么事?”

在过去的一年里,这是行得通的。但随着开源模型(如 GLM、Qwen、MiniMax、DeepSeek、KIMI等)的全面爆发,以及企业对数据隐私、成本控制的极致追求,“本地化/私有化部署大模型” 也正在成为一些中大型企业的刚需。

当你作为架构师或后端主力,被老板要求把一个 70B 的大模型部署到公司的服务器上时,真正的挑战才刚刚开始:

  • 面对 OOM(显存溢出),你该如何调整参数?
  • 并发量稍微一高,首字延迟(TTFT)就卡到几十秒,你该怎么排查?
  • 采购硬件时,你是买 8 张便宜的 RTX 4090,还是花高价租用带 NVLink 的 A100/H100?
  • 你该如何向团队解释引入 vLLM、FlashAttention 和 INT8/FP8 量化的必要性?

如果你把 GPU 当成一个“跑得更快的 CPU”来用,你将会在上述每一个问题上栽大跟头。

你需要建立一套全新的“硬件心智模型”,这也是我编写这门《AI 工程师的 GPU 入门课:从硬件视角看大模型推理》微专栏的主要目标。

这门微专栏将教你什么?

市面上关于 GPU 和 CUDA 的教程很多,但大多是教你如何写出复杂的 C++ 图形渲染代码,或者如何在学术上推导矩阵乘法。

这门微专栏与众不同。它是专为后端/软件工程师打造的“白盒化” GPU 入门课程。

我们不教图形渲染,不深究复杂的 C++ 语法。我们将直接切入大模型推理的痛点,带你一步步从物理架构走到前沿的 AI 工程技术。

  • 如果你想吃透热门技术: 我们将为你讲透 FlashAttention、PagedAttention (vLLM)、模型量化背后的物理原理。你会发现,这些看似高深的技术,本质上都是在和“内存墙”做斗争。
  • 如果你追求实战落地: 我们不仅教你看懂硬件,还会教你用 Profiling 工具(性能分析器)像侦探一样排查慢查询;作为加餐,我们甚至会教你如何用纯 Go 语言(Zero CGO)直接点火发射 CUDA 内核!

课程目录全景图

为了让你对这趟旅程有一个清晰的预期,以下是本专栏的完整地图:

第一阶段:硬件心智模型
* 第 01 讲 | 硬件解剖:为什么 CPU 是“法拉利”,GPU 是“大巴车”?(含 5090 vs H100 对比)
* 第 02 讲 | 内存金字塔:HBM、SRAM 与不可逾越的“内存墙”

第二阶段:编程模型与工具链
* 第 03 讲 | CUDA 编程模型:指挥“千军万马”的线程艺术
* 第 04 讲 | 性能侦探:性能侦探:拆解 Hello World Kernel 与 Profiling 实战

第三阶段:AI 工程进阶
* 第 05 讲 | 显存管理革命:从 KV Cache 到 PagedAttention (vLLM)
* 第 06 讲 | 算子融合魔法:FlashAttention 的底层原理
* 第 07 讲 | 精度与量化:精度与量化:INT8/FP8 为什么既快又省?
* 第 08 讲 | 分布式推理:Tensor Parallelism (TP) 与通信墙
* 第 09 讲 | 终极指南:如何科学计算 AI 算力需求与硬件选型?

特别加餐:Gopher 的专属浪漫
* 第 10 讲 | 加餐:Go 语言的 GPU 编程——Gopher 的逆袭

小结

在算力的装备竞赛里,最锋利的武器未必是更昂贵的芯片,而是深刻理解软硬件边界的人。

正如谷歌 TurboQuant 证明的那样:懂底层的工程师,只需改写一行底层逻辑,就可能撬动万亿级别的市场价值。

算力时代,不要只做“调包”的局外人。

准备好跨越 CPU 的舒适区,跟我一起深入算力的硅基心脏了吗?

点击这里或扫描下方二维码,开启你的GPU与AI推理工程的入门之旅:

我将在第一讲等你。


还在为“复制粘贴喂AI”而烦恼?我的新专栏 AI原生开发工作流实战 将带你:

  • 告别低效,重塑开发范式
  • 驾驭AI Agent(Claude Code),实现工作流自动化
  • 从“AI使用者”进化为规范驱动开发的“工作流指挥家”

扫描下方二维码,开启你的AI原生开发之旅。


原「Gopher部落」已重装升级为「Go & AI 精进营」知识星球,快来加入星球,开启你的技术跃迁之旅吧!

我们致力于打造一个高品质的 Go 语言深度学习AI 应用探索 平台。在这里,你将获得:

  • 体系化 Go 核心进阶内容: 深入「Go原理课」、「Go进阶课」、「Go避坑课」等独家深度专栏,夯实你的 Go 内功。
  • 前沿 Go+AI 实战赋能: 紧跟时代步伐,学习「Go+AI应用实战」、「Agent开发实战课」、「Agentic软件工程课」、「Claude Code开发工作流实战课」、「OpenClaw实战分享」等,掌握 AI 时代新技能。
  • 星主 Tony Bai 亲自答疑: 遇到难题?星主第一时间为你深度解析,扫清学习障碍。
  • 高活跃 Gopher 交流圈: 与众多优秀 Gopher 分享心得、讨论技术,碰撞思想火花。
  • 独家资源与内容首发: 技术文章、课程更新、精选资源,第一时间触达。

衷心希望「Go & AI 精进营」能成为你学习、进步、交流的港湾。让我们在此相聚,享受技术精进的快乐!欢迎你的加入!

img{512x368}


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

© 2026, bigwhite. 版权所有.

🔲 ☆

被嘲笑比 Python 还慢?扒开 Go 正则表达式的底层,看看它为了防范“系统猝死”付出了什么

本文永久链接 – https://tonybai.com/2026/03/17/why-is-go-regex-so-slow

大家好,我是Tony Bai。

如果有人问你:在处理纯 CPU 密集型的文本匹配时,Go 和 Python 哪个快?

相信 99% 的 Go 开发者会毫不犹豫地把票投给 Go。毕竟,一门编译型的静态语言,怎么可能输给拖着 GIL 锁的解释型脚本语言?

但现实往往比小说更魔幻。

最近,在 Reddit 的 r/golang 论坛上,一张残酷的 Benchmark 跑分图引发了整个 Go 社区的剧烈震荡。一位开发者,使用极其常见的日志解析正则表达式(提取 IP、时间、URI 等),对各大语言进行了一次横评。

结果令人大跌眼镜:同样的数据集,Rust 跑了 3.9 秒,Zig 跑了 1.3 秒,而 Go 居然跑了整整 38.1 秒!整整比第一名 Zig 慢了接近 30 倍!

如果你再去翻看 Go 官方的 Issue #26623,会看到更绝望的数据:早在2018年的一次正则基准测试中,Go 不仅被 C++ 和 Rust 碾压,甚至连 Python 3、PHP 和 Javascript 都能在正则上把 Go 按在地上摩擦。

一时间,无数 Gopher 信仰崩塌:“为什么 Go 的标准库 regexp 这么慢?”、“连简单的正则都做不好,Go 凭什么做云原生霸主?”

今天,我们就来硬核扒开 Go 语言 regexp 包的底层设计和实现。你会发现,这不是 Go 团队的技术拉跨,而是一场关于“性能、安全与工程哲学”的博弈。

原罪:你以为的慢,其实是替 CGO 负重前行

面对“为什么 Go 的正则比 Python 还慢”的灵魂拷问,Go 核心团队成员 Ian Lance Taylor 给出了第一层解释。

在 Python、PHP 甚至 Node.js 中,你以为你是在运行脚本,其实它们底层都在悄悄“作弊”。这些语言的正则表达式引擎,几乎全部是用高度优化的 C 语言库(主要是 PCRE,Perl Compatible Regular Expressions)编写的。

当你在 Python 里调用 re.match() 时,它瞬间就穿透到了 C 语言的底层,享受着现代 CPU 指令集的极致加速。

那 Go 为什么不用 C?因为 Go 是一门有着“极度洁癖”的语言。

如果 Go 的标准库引入了 C 语言的 PCRE,就必须通过 CGO 来调用。而 CGO 的上下文切换成本极高,更致命的是,它会彻底破坏 Go 引以为傲的“跨平台交叉编译”能力。你再也不能在一个简单的 go build 后,把二进制文件无痛丢到任何 Alpine 容器里了。

因此,Go 团队做出了第一个艰难的决定:完全使用纯 Go 语言,从零手写一个正则表达式引擎。

脱离了 C 语言几十年的底层优化积累,用原生代码去硬刚别人的 C 引擎,这是 Go 看起来“慢”的表层原因。

但这,仅仅是冰山一角。

路线之争:为了防止系统“猝死”,Go 抛弃了速度

真正让 Go 正则变得“慢”的,是算法架构上的降维选择。这牵扯到 Go 语言的缔造者之一、大神 Russ Cox (rsc) 的一段往事。

在正则表达式的底层世界里,存在着两大流派:

  1. 基于回溯(Backtracking)的 NFA 引擎:代表人物是 PCRE(被 Python、Java、PHP 广泛使用)。
  2. 基于 Thompson NFA / DFA 的引擎:代表人物是 RE2(被 Go、Rust 采用)。

PCRE 引擎极快,它支持各种花里胡哨的语法(如前瞻断言 Lookaround、反向引用 Backreferences)。它的算法逻辑是“不撞南墙不回头”的深度优先搜索(DFS)。在匹配正常字符串时,它快如闪电。

但它有一个极其致命的死穴:ReDoS(正则表达式拒绝服务攻击)。

想象一下你写了一个看似无害的正则:

^([a-zA-Z0-9]+\s?)+$

如果黑客故意传入一个极其恶意的字符串:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa!(注意最后的感叹号)。

PCRE 引擎会陷入可怕的“灾难性回溯”。它会尝试所有可能的组合,时间复杂度瞬间飙升到 O(2^n) 级。短短几十个字符的输入,能让单核 CPU 满载运行几年都算不出结果!

2019 年,互联网巨头 Cloudflare 就因为在 WAF 防火墙中写错了一个极其简单的正则表达式,CPU资源瞬间耗尽,导致全球80% 的通过 Cloudflare 代理的网站受到影响,陷入瘫痪长达 27 分钟。这就是 PCRE 回溯引擎的恐怖破坏力。

Russ Cox 在设计 Go 的 regexp 包时,定下了一条铁律:系统安全与可预测性,绝对高于单次请求的极限性能。

因此,Go 彻底抛弃了危险的回溯引擎,选择了基于 Thompson NFA 的算法(源自他之前在Google主导设计的 C++ RE2 引擎)。这种算法保证了匹配时间永远是线性复杂度 O(n)

无论黑客传入多么恶意的字符串,Go 的正则引擎绝对不会发生灾难性回溯。它牺牲了在美好情况下的极致快感,换取了在极端恶劣环境下的金身不坏。

这算是 Go 团队最顶级的“克制”吧。

硬核剖析:Go 的正则,时间到底去哪了?

既然算法是 O(n) 的,为什么 Go 依然比同样采用 RE2/DFA 思想的 Rust 慢那么多呢?

如果你去追踪 Go 官方的 Issue #19629Issue #11646,通过 pprof 分析 Go 正则匹配的 CPU 耗时,你会看到几个令人头疼的瓶颈:

1. 沉重的 UTF-8 解析税

Rust 和 C 的很多正则引擎,底层是直接在“字节(Byte)”级别游走的。而 Go 为了贯彻它对 Unicode 的原生支持,regexp 包在内部极其频繁地将输入流解码为 Rune(Go 的 Unicode 字符单位)。这种逐个解析 Rune 的操作,带来了巨大的计算开销。

2. NFA 虚拟线程的内存震荡

在 Go 的底层源码中,你可以看到耗时最高的两个函数是 (machine).add 和 (machine).step。

Go 是通过维护两个“状态队列(稀疏集)”来模拟 NFA 的并行推进的。每读取一个字符,引擎就要把所有可能的状态添加到下一个队列中。这导致了海量的内存重分配(Allocation)和切片拷贝。哪怕是匹配一个简单的长字符串,底层都在疯狂地挪动内存。

既然这么慢,为什么不把 C++ RE2 里那个极速的 DFA(确定性有限状态自动机)移植到 Go 里呢?

Issue #11646 记录了这次尝试。开发者 Michael Matloob 曾经试图将 RE2 的 DFA 移植过来,但被 Russ Cox 拦下了。原因很直接:DFA 虽然快,但它在运行时会动态生成大量的状态,如果不加以严格限制,极易引发内存耗尽(OOM)。在 Go 带有 GC 的内存模型下,频繁创建和销毁庞大的 DFA 状态缓存,会让垃圾回收器不堪重负。

于是,Go 的标准库在“安全、内存、性能”的三角博弈中,选择了妥协于现状。

社区的探索:SIMD 降维打击与 100倍加速的 coregex

官方的克制固然令人敬佩,但对于身处一线的业务开发者来说,由于正则太慢导致的 CPU 告警,是实实在在的痛点。

“既然官方不愿意改,那我们就自己造轮子!”

在近期的 Issue #26623 中,一位名为 kolkov 的开发者带着他的开源库 coregex 杀入了战场,向 Go 标准库发起了直接的挑战。

coregex 是一个完全用纯 Go 编写的正则库,它的出现直接将 Go 的正则性能拉到了与 Rust 并驾齐驱,甚至在某些场景下超越 Rust 的境地。

它是怎么做到的?它在底层祭出了几个大杀器:

  1. SIMD 预过滤(Prefilters):它使用了手写的汇编代码(AVX2/SSSE3 指令集),将正则中的静态字符串提取出来,利用 CPU 的向量化指令,一次性对比 32 个字节。像匹配 .*.txt 这种正则,速度直接飙升了 1500倍
  2. 带缓存的 Lazy DFA:它绕过了标准库每次都重算 NFA 的毛病,在运行时动态构建 DFA 缓存,大幅消除了内存分配。
  3. 写时复制(COW)的捕获组:标准库在处理提取子串时会疯狂分配切片。coregex 通过切片状态共享,让内存分配直接减少了 50%。

在 kolkov 提供的 CI 跑分中,在 6MB 的输入下,coregex 处理邮箱、URI 的耗时仅为 1.5 毫秒,而标准库耗时高达 260 毫秒。足足快了 170 倍!

然而,这段极其硬核的改进,依然很难入Go团队法眼,更不用谈在短期内被合并进 Go 的标准库。

一方面,Go 官方目前正在推进自己的内建 SIMD 方案(Issue #73787),不想接入手写的汇编代码;另一方面,社区大牛 Ben Hoyt 在使用 coregex 时发现,如果开启 Longest() 模式(最长匹配模式),这个库的性能会发生严重退化。

这再次印证了标准库开发的残酷:在某几个特定场景下跑到全宇宙第一很容易,但要在一套 API 里无死角地兜底全世界所有的奇葩正则输入,难如登天。

在 Go 中写正则的正确姿势

大致了解了底层原理,回到日常开发中,我们该如何应对 Go 正则的性能瓶颈?作为高级 Go 开发者,请务必将以下三条军规刻在脑子里:

第一条:能不用正则,就坚决不用

如果你只是想检查字符串是否包含子串,或者进行简单的前后缀匹配,永远优先使用 strings.Contains()、strings.HasPrefix() 等内置函数。 它们底层有优化的实现,在这样简单场景下,速度是 regexp 包不可比拟的。

第二条:将编译前置,远离循环

如果你翻看新手代码,最常见的低级错误就是在 for 循环或者每次 HTTP 请求里调用 regexp.Compile()。

正则的编译过程(生成 NFA 字节码)极其消耗 CPU。请永远在全局变量或 init() 函数中使用 regexp.MustCompile(),将其编译好并复用。Go 的 Regexp 对象是并发安全的,随便多 Goroutine 调用。

第三条:在极端性能要求下,打破“洁癖”

如果你的核心业务(比如高频日志清洗、海量数据 ETL)确实被 regexp 卡住了脖子,不要硬抗。

你可以选择引入通过 CGO 调用 PCRE的Go binding库(比如https://github.com/GRbit/go-pcre),但要注意防范 ReDoS 攻击,或google/re2的Go binding(比如https://github.com/wasilibs/go-re2),又或是在业务侧尝试社区的野路子 coregex。在生存面前,架构的“洁癖”是可以适当妥协的。

小结

“为什么 Go 的正则这么慢?”

这并非一个简单的工程失误。它是一道分水岭,隔开了“追求跑分好看的玩具代码”与“守护千万级并发集群的生产级设计”。

Russ Cox 宁愿忍受整个开源界的群嘲,也没有为了刷榜而去引入危险的回溯引擎。这或许就是 Go 语言能够成为云原生时代头部语言的原因:不盲目追求上限的巅峰,而是死死守住安全下限。

参考资料

  • https://www.reddit.com/r/golang/comments/1rr2evh/why_is_gos_regex_so_slow/
  • https://github.com/golang/go/issues/26623
  • https://github.com/golang/go/issues/19629
  • https://github.com/golang/go/issues/11646
  • https://swtch.com/~rsc/regexp/

今日互动探讨:

在你的日常开发中,有没有被由于“写了糟糕的正则表达式”而导致 CPU 飙升 100% 的惨痛经历?你又是如何排查和优化的?

欢迎在评论区分享你的血泪史


认知跃迁:读懂底层机制,才能看透系统架构的本质

从放弃 CGO 选择纯 Go 实现,到防范 ReDoS 采用 NFA,再到社区为了榨干 CPU 性能而引入 SIMD。Go 语言的每一个看似“不合理”的设计背后,都隐藏着深邃的系统级考量。

然而,令人遗憾的是,很多开发者写了五六年的 Go 代码,遇到性能瓶颈依然只能靠“瞎猜”和“重启”。他们对 Go 的内存逃逸、Goroutine 调度机制以及标准库的底层数据结构一无所知。

如果你渴望突破“熟练调包侠”的瓶颈,想要像 Russ Cox 这样的顶级大厂架构师一样,看透 Go 语言背后的底层逻辑,建立起自己坚不可摧的技术护城河——

我的极客时间专栏 Tony Bai·Go语言进阶课 正是为你量身定制。

在这 30+ 讲极其硬核的内容中,我不仅带你剥开语法糖,深挖 Goroutine 调度、Channel 哲学;更会带你全面吃透 Go 的工程化实践,把底层性能调优背后的逻辑一次性讲透。

目标只有一个:助你完成从“Go 熟练工”到“能做顶级架构决策的 Go 专家”的蜕变!

扫描下方二维码,加入专栏。不要用战术上的勤奋,掩盖战略上的懒惰。让我们一起用架构师的视角,重新认识 Go 语言。


还在为“复制粘贴喂AI”而烦恼?我的新专栏 AI原生开发工作流实战 将带你:

  • 告别低效,重塑开发范式
  • 驾驭AI Agent(Claude Code),实现工作流自动化
  • 从“AI使用者”进化为规范驱动开发的“工作流指挥家”

扫描下方二维码,开启你的AI原生开发之旅。


原「Gopher部落」已重装升级为「Go & AI 精进营」知识星球,快来加入星球,开启你的技术跃迁之旅吧!

我们致力于打造一个高品质的 Go 语言深度学习AI 应用探索 平台。在这里,你将获得:

  • 体系化 Go 核心进阶内容: 深入「Go原理课」、「Go进阶课」、「Go避坑课」等独家深度专栏,夯实你的 Go 内功。
  • 前沿 Go+AI 实战赋能: 紧跟时代步伐,学习「Go+AI应用实战」、「Agent开发实战课」、「Agentic软件工程课」、「Claude Code开发工作流实战课」、「OpenClaw实战分享」等,掌握 AI 时代新技能。
  • 星主 Tony Bai 亲自答疑: 遇到难题?星主第一时间为你深度解析,扫清学习障碍。
  • 高活跃 Gopher 交流圈: 与众多优秀 Gopher 分享心得、讨论技术,碰撞思想火花。
  • 独家资源与内容首发: 技术文章、课程更新、精选资源,第一时间触达。

衷心希望「Go & AI 精进营」能成为你学习、进步、交流的港湾。让我们在此相聚,享受技术精进的快乐!欢迎你的加入!

img{512x368}


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

© 2026, bigwhite. 版权所有.

🔲 ☆

Go 1.26 重磅更新:用 go fix 重塑代码现代化的艺术

本文永久链接 – https://tonybai.com/2026/02/19/using-go-fix-to-modernize-go-code

大家好,我是Tony Bai。

2026年2月,Go 1.26 正式发布。除了语言层面的新特性(如 new(expr))和运行时的性能提升(如 Green Tea GC)之外,工具链迎来了一次史诗级的升级:go fix 命令被彻底重写。

在过去,go fix 更多是用来解决破坏性变更的“补救工具”(例如 Go 1.4 到 Go 1.5 的迁移)。但在 Go 1.26 中,它华丽转身,成为了一个代码现代化(Modernization)的利器。它不再仅仅是修复错误,而是主动帮助你将代码升级到 Go 的最新惯用法(Idioms)。

本文将基于 Alan Donovan 的官方博文,深度解析新版 go fix 的工作原理、核心特性——Modernizers(现代化器),以及其背后的分析框架架构。旨在帮助你彻底掌握这一新工具,让你的 Go 代码库焕发新生。

背景

随着 Go 语言进入“后泛型时代”(Post-Go 1.18),语言特性的演进速度明显加快。从 strings.Cut 到 min/max 内置函数,再到 range-over-func,每一个版本都在引入更简洁、更高效的表达方式。

然而,现实是残酷的:代码库具有巨大的惯性

大多数现存的 Go 代码依然停留在几年前的写法上。更糟糕的是,随着 LLM(大语言模型)编程助手的普及,AI 正在基于海量的旧代码进行训练。这就导致了一个恶性循环:AI 学习了旧的写法,生成了旧的写法,开发者接受了旧的写法,进一步污染了语料库。

Go 团队意识到了这一点。为了打破这个循环,确保未来的模型和新加入的开发者能够掌握最新的 Go 习惯用法,Go 1.26 推出了全新的 go fix。它利用了一套复杂的静态分析算法,自动识别并重构代码,使其拥抱现代化的 Go。

go fix 的全新打开方式

新版的 go fix 在使用体验上向 go build 和 go vet 看齐。它接受标准的包模式(Package Patterns)。

1. 基础用法

要“修复”当前目录及其子目录下的所有包,只需运行:

$ go fix ./...

如果运行成功,它会静默地直接修改你的源文件。

注意:go fix 会自动忽略生成的文件(Generated Files),因为对生成文件的修复应该在生成器本身中进行,而不是在产物中。

2. 预览变更:-diff

由于 go fix 可能会瞬间修改成百上千个文件,直接运行可能让人心惊肉跳。Go 团队贴心地提供了 -diff 标志,让你在应用变更前先进行预览:

$ go fix -diff ./...
--- dir/file.go (old)
+++ dir/file.go (new)
- eq := strings.IndexByte(pair, '=')
- result[pair[:eq]] = pair[1+eq:]
+ before, after, _ := strings.Cut(pair, "=")
+ result[before] = after
...

因此,我们强烈建议每次升级 Go 工具链版本后,都对项目运行一次 go fix。在运行前,请确保 Git 工作区是干净的,这样你可以清晰地查看 go fix 带来的改动,并方便同事进行 Code Review。

3. 选择性执行

默认情况下,go fix 会运行所有注册的分析器。但在大型项目中,为了减轻 Code Review 的负担,你可能希望一次只应用一种类型的修复。

你可以通过 go tool fix help 查看所有可用的分析器:

$go tool fix help
fix is a tool for static analysis of Go programs.

fix examines Go source code and reports diagnostics for
suspicious constructs or opportunities for improvement.
Diagnostics may include suggested fixes.

An example of a suspicious construct is a Printf call whose arguments
do not align with the format string. Analyzers may use heuristics that
do not guarantee all reports are genuine problems, but can find
mistakes not caught by the compiler.

An example of an opportunity for improvement is a loop over
strings.Split(doc, "\n"), which may be replaced by a loop over the
strings.SplitSeq iterator, avoiding an array allocation.
Diagnostics in such cases may report non-problems,
but should carry fixes that may be safely applied.

For analyzers of the first kind, use "go vet -vettool=PROGRAM"
to run the tool and report diagnostics.

For analyzers of the second kind, use "go fix -fixtool=PROGRAM"
to run the tool and apply the fixes it suggests.

Registered analyzers:

    any          replace interface{} with any
    buildtag     check //go:build and // +build directives
    fmtappendf   replace []byte(fmt.Sprintf) with fmt.Appendf
    forvar       remove redundant re-declaration of loop variables
    hostport     check format of addresses passed to net.Dial
    inline       apply fixes based on 'go:fix inline' comment directives
    mapsloop     replace explicit loops over maps with calls to maps package
    minmax       replace if/else statements with calls to min or max
    newexpr      simplify code by using go1.26's new(expr)
    omitzero     suggest replacing omitempty with omitzero for struct fields
    plusbuild    remove obsolete //+build comments
    rangeint     replace 3-clause for loops with for-range over integers
    reflecttypefor replace reflect.TypeOf(x) with TypeFor[T]()
    slicescontains replace loops with slices.Contains or slices.ContainsFunc
    slicessort   replace sort.Slice with slices.Sort for basic types
    stditerators use iterators instead of Len/At-style APIs
    stringsbuilder replace += with strings.Builder
    stringscut   replace strings.Index etc. with strings.Cut
    stringscutprefix replace HasPrefix/TrimPrefix with CutPrefix
    stringsseq   replace ranging over Split/Fields with SplitSeq/FieldsSeq
    testingcontext replace context.WithCancel with t.Context in tests
    waitgroup    replace wg.Add(1)/go/wg.Done() with wg.Go

By default all analyzers are run.
... ...

要查看特定分析器的文档:

$ go tool fix help forvar
forvar: remove redundant re-declaration of loop variables

The forvar analyzer removes unnecessary shadowing of loop variables.
Before Go 1.22, it was common to write for _, x := range s { x := x ... }
to create a fresh variable for each iteration. Go 1.22 changed the semantics
of for loops, making this pattern redundant. This analyzer removes the
unnecessary x := x statement.

This fix only applies to range loops.

要单独运行某个分析器(例如 any),可以使用对应的标志:

$ go fix -any ./...

反之,如果你想运行除了 any 之外的所有分析器,可以将其禁用:

$ go fix -any=false ./...

4. 交叉平台修复

和 go vet 一样,go fix 也是基于特定的构建配置(Build Configuration)进行分析的。如果你的项目包含大量特定于平台的文件(例如 _linux.go, _windows.go),建议针对不同的 GOOS 和 GOARCH 多次运行:

$ GOOS=linux   GOARCH=amd64 go fix ./...
$ GOOS=darwin  GOARCH=arm64 go fix ./...
$ GOOS=windows GOARCH=amd64 go fix ./...

核心特性:Modernizers(现代化器)

Go 1.26 引入了一个新概念:Modernizers。它们是一组特殊的分析器,专门用于将旧的习惯用法替换为利用新语言特性或新标准库 API 的写法。

以下是几个最具代表性的 Modernizers 示例,展示了它们如何简化代码:

1. minmax:拥抱内置函数

在 Go 1.21 之前,计算最小值/最大值通常需要写冗长的 if/else 语句。

旧代码:

x := f()
if x < 0 {
    x = 0
}
if x > 100 {
    x = 100
}

minmax 修复后可能的样子:

x := min(max(f(), 0), 100)

代码意图一目了然,且消除了分支跳转,可能带来微小的性能提升。

2. rangeint:告别 C 风格循环

Go 1.22 引入了对整数的 range 支持。

旧代码:

for i := 0; i < n; i++ {
    f()
}

rangeint 修复后:

for range n {
    f()
}

如果你不需要索引 i,新的写法极其清爽。

3. stringscut:字符串分割的最佳实践

Go 1.18 引入的 strings.Cut 是处理“按分隔符切分”场景的神器,它比 Index + Slicing 更高效且不易出错。

旧代码:

i := strings.Index(s, ":")
if i >= 0 {
    return s[:i]
}

stringscut 修复后:

before, _, ok := strings.Cut(s, ":")
if ok {
    return before
}

4. newexpr:Go 1.26 的专属语法糖

这是 Go 1.26 刚刚引入的语言变动:new() 函数现在支持传入表达式,直接初始化变量。这在处理 Protobuf 或 JSON 的可选字段(Pointer 类型)时非常有用。

旧代码(通常需要辅助函数):

func newInt(x int) *int { return &x }

data, err := json.Marshal(&RequestJSON{
    URL: url,
    Attempts: newInt(10), // 需要定义辅助函数或临时变量
})

newexpr 修复后:

data, err := json.Marshal(&RequestJSON{
    URL: url,
    Attempts: new(10), // Go 1.26 原生支持!
})

newexpr 这样的 Modernizer 非常智能。它会检查你的 go.mod 文件中的 go 指令或文件的 //go:build 标签。只有当你的项目明确声明支持 Go 1.26 或更高版本时,它才会建议由于 new(expr) 带来的修改。这确保了 go fix 不会引入破坏向后兼容性的代码。

协同效应与冲突解决

go fix 的强大之处在于它是迭代式的。应用一个修复可能会触发另一个修复。

协同效应(Synergy)示例

考虑一个经典的性能陷阱:在循环中拼接字符串。

初始代码:

s := ""
for _, b := range bytes {
    s += fmt.Sprintf("%02x", b) // O(N^2) 复杂度!
}
use(s)

第一轮 go fix (stringsbuilder):

分析器识别出这是低效的字符串拼接,将其重构为 strings.Builder。

var s strings.Builder
for _, b := range bytes {
    s.WriteString(fmt.Sprintf("%02x", b))
}
use(s.String())

第二轮 go fix (fmtappendf):

一旦代码变成了 WriteString(Sprintf(…)),另一个分析器(源自 staticcheck 的 QF1012)就会识别出这可以优化为 fmt.Fprintf,不仅更简洁,而且直接写入 Buffer,减少了中间内存分配。

var s strings.Builder
for _, b := range bytes {
    fmt.Fprintf(&s, "%02x", b)
}
use(s.String())

因此,对于大型重构,建议运行多次 go fix,直到代码达到稳定态(Fixed Point)。

冲突处理

go fix 可能会在同一文件的不同位置应用几十个修复。它内部使用了一个简单的三路合并算法(Three-way Merge)来协调这些修改。如果两个修复在语法上冲突(例如修改了同一行),工具会丢弃其中一个,并提示用户重新运行。

但还有一种更棘手的语义冲突(Semantic Conflict)。

例如,修复 A 删除了变量 x 的一次使用,修复 B 删除了 x 的另一次使用。两个修复单独看都没问题,但合在一起后,变量 x 变成了“未使用的变量”,导致编译错误。

go fix 的解决方案很务实:它在所有修复应用完毕后,会运行一个最终的清理 Pass,自动删除那些因重构而变得多余的 import 语句。对于未使用的变量,通常会留给编译器报错,由开发者手动删除(或者等待未来的 deadcode 消除器)。

幕后英雄:Go 分析框架 (The Analysis Framework)

新版 go fix 的核心动力来自于 Go Analysis Framework

历史沿革

早在 2017 年,Go 团队将 go vet 的核心逻辑拆分成了两部分:

  1. Analyzers(分析器):纯粹的算法逻辑,负责发现问题(Checker)或建议修复(Fixer)。
  2. Drivers(驱动器):负责加载程序、运行分析器并展示结果。

这种分离架构带来了极大的灵活性。同一个分析器(比如 printf 检查)可以运行在多种场景下:

  • unitchecker:go vet 和 go fix 的底层驱动,支持增量构建。
  • gopls:Go 语言服务器,在编辑器中实时提供红色波浪线和快速修复(Quick Fix)。
  • nogo:用于 Bazel 等构建系统的驱动。
  • analysistest:用于测试分析器本身的框架。

Go 1.26 的里程碑意义在于:go fix 和 go vet 在底层实现上终于完全统一了。 它们的区别仅在于目标:vet 侧重于报告错误(低误报率),fix 侧重于自动修改(无回退,保全正确性)。

性能黑科技

为了让 go fix 能在大型代码库上秒级运行,Go 团队引入了多项基础设施优化:

  1. Inspector 与 Cursor
    分析器通常需要遍历语法树(AST)。inspector 包预先计算了遍历索引,使得分析器可以快速跳过不关心的节点。新增的 Cursor 类型更是允许在 AST 上进行类似 DOM 的灵活导航(父节点、兄弟节点)。

  2. Facts(事实)与跨包推断
    分析框架支持跨包的“事实”传递。例如,printf 检查器可以分析 log.Printf 的函数体,得出一个“Fact”:log.Printf 是 fmt.Printf 的包装器。这个 Fact 会被序列化并传递给导入了 log 包的其他包,从而实现跨包的格式化字符串检查。

  3. TypeIndex(类型索引)
    很多分析器需要查找“所有对 fmt.Printf 的调用”。与其遍历整个 AST,typeindex 预先构建了符号引用索引。这使得查找特定符号的开销从“与代码量成正比”降低为“与调用次数成正比”,对于查找冷门符号(如 net.Dial)的分析器,性能提升可达 1000 倍

未来展望:“自助式”分析 (Self-Service)

Alan Donovan 在博文中提出了一个令人兴奋的愿景:Self-Service Paradigm(自助式范式)

目前的 Modernizers 大多是针对 Go 标准库的。但第三方库的作者呢?如果你维护了一个流行的 ORM 或 Web 框架,当你升级 API 时,如何帮助你的用户自动迁移?

你不可能把你的迁移逻辑塞进 Go 官方的 go fix 里。

Go 1.26 迈出了“自助服务”的第一步:基于注解的内联器(Annotation-driven Inliner)

//go:fix inline

库作者可以在即将废弃的函数上添加一行特殊的注释:

// Deprecated: Use Pow(x, 2) instead.
//go:fix inline
func Square(x int) int { return Pow(x, 2) }

当用户运行 go fix 时,分析器会识别这个指令,并自动将用户代码中的 Square(x) 替换为 Pow(x, 2)。

未来的可能性

  1. 动态加载分析器
    未来,Go 可能会支持从模块源代码树中动态加载分析器并安全执行。这意味着 sql 包可以自带一个检查器来防止 SQL 注入,或者你的公司内部框架可以自带一套 go fix 规则来强制执行内部编码规范。

  2. 声明式控制流检查
    许多检查逻辑都遵循“做完 Y 之后别忘了 X”的模式(例如:打开文件后别忘了 Close,获取锁后别忘了 Unlock)。Go 团队计划探索一种通用的方式,让开发者只需简单的注解就能定义这种检查,而无需编写复杂的 Go 代码来分析控制流。

小结

Go 1.26 的 go fix 不仅仅是一个工具的更新,它代表了 Go 工程化能力的一次跃迁。

它告诉我们:维护代码不仅是修修补补,更是持续的进化。 通过将最佳实践固化为代码(Analyzers),并赋予工具自动执行的能力(Fixers),Go 正在构建一个更加健康、更具韧性的生态系统。

对于每一位 Gopher 来说,现在的任务很简单:升级到 Go 1.26(记得将go.mod的go版本升级为go 1.26.0或后续版本),在你的项目中运行 go fix ./…,然后享受代码变得更现代、更高效的快感吧。

参考资料:https://go.dev/blog/gofix


你的“现代化”阻碍是什么?

自动重构工具虽然强大,但老代码库的惯性依然巨大。在你目前的项目中,有哪些“旧习惯”最让你难以割舍?你是否尝试过用 go fix 来升级你的代码?

欢迎在评论区分享你的重构经历或对新工具的看法!


还在为“复制粘贴喂AI”而烦恼?我的新专栏 AI原生开发工作流实战 将带你:

  • 告别低效,重塑开发范式
  • 驾驭AI Agent(Claude Code),实现工作流自动化
  • 从“AI使用者”进化为规范驱动开发的“工作流指挥家”

扫描下方二维码,开启你的AI原生开发之旅。


你的Go技能,是否也卡在了“熟练”到“精通”的瓶颈期?

  • 想写出更地道、更健壮的Go代码,却总在细节上踩坑?
  • 渴望提升软件设计能力,驾驭复杂Go项目却缺乏章法?
  • 想打造生产级的Go服务,却在工程化实践中屡屡受挫?

继《Go语言第一课》后,我的《Go语言进阶课》终于在极客时间与大家见面了!

我的全新极客时间专栏 《Tony Bai·Go语言进阶课》就是为这样的你量身打造!30+讲硬核内容,带你夯实语法认知,提升设计思维,锻造工程实践能力,更有实战项目串讲。

目标只有一个:助你完成从“Go熟练工”到“Go专家”的蜕变! 现在就加入,让你的Go技能再上一个新台阶!


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

© 2026, bigwhite. 版权所有.

🔲 ☆

AI 基础设施的语言之争:为何构建 LLM 网关时,我们放弃了 Python 选择了 Go?

本文永久链接 – https://tonybai.com/2026/02/18/why-we-chose-go-over-python-for-llm-gateways

大家好,我是Tony Bai。

在 2026 年的今天,人工智能早已走出了实验室,成为企业级应用的核心驱动力。Python,凭借其在机器学习领域的绝对统治地位——拥有 PyTorch、TensorFlow、Hugging Face 等无可匹敌的生态系统——长期以来被视为 AI 开发的“默认语言”。

然而,随着 AI 应用从模型训练(Training)走向推理服务(Inference)和应用编排(Orchestration),工程重心发生了微妙的转移。当我们谈论模型本身时,Python 是王者;但当我们谈论承载模型流量的基础设施——网关、代理、路由器时,Python 还是最佳选择吗?

近日,开源 LLM 网关项目 Bifrost 的维护者在 Reddit 上分享了一篇题为《Why we chose Go over Python for building an LLM gateway》的技术复盘,引发了社区的强烈反响。他们放弃了拥有 LiteLLM 等成熟竞品的 Python 生态,转而使用 Go 重写了核心网关。结果令人咋舌:延迟降低了约 700 倍,内存占用降低了 68%,吞吐量提升了 3 倍。

这场技术选型的背后,折射出的是 AI 工程化进入深水区后,对并发模型、资源效率与部署架构的重新审视。

Python 的“舒适区”与“性能墙”

在项目的初期,选择 Python 似乎是理所当然的。

1. 生态惯性与“胶水”优势

绝大多数 AI 工程师都是 Python Native。从 LangChain 到 LlamaIndex,几乎所有的 Agent 开发框架都优先支持 Python。使用 Python 构建网关,意味着可以直接复用现有的库,甚至可以直接挂载一些轻量级的 Python 逻辑来处理 Embeddings 或 RAG(检索增强生成)流程。FastAPI 的易用性更是让开发者能在几分钟内搭建起一个服务。

2. 遭遇瓶颈:网关的本质是 I/O

然而,LLM 网关的业务属性决定了它的性能痛点。与计算密集型(CPU-bound)的模型推理不同,网关是典型的 I/O 密集型应用。它的核心职责是:

  • 接收成千上万的客户端请求。
  • 将请求转发给上游提供商(如 OpenAI, Anthropic, 或自建的 vLLM)。
  • 等待上游响应(这是最耗时的环节,LLM 的首字延迟 TTFT 通常在秒级)。
  • 将流式响应(SSE)回传给客户端。

在这个过程中,网关绝大部分时间都在“等待”。

3. Python 的并发痛点

Bifrost 团队在测试中发现,当并发请求数达到 500-1000 RPS(每秒请求数)时,Python 的瓶颈开始显现。

  • GIL(全局解释器锁)的幽灵:虽然 Python 的 asyncio 可以处理 I/O 并发,但 GIL 依然限制了多核 CPU 的利用率。对于需要处理大量并发连接、同时可能涉及少量数据处理(如 Token 计数、PII 过滤)的网关来说,线程竞争(Thread Contention)成为了不可忽视的开销。
  • 昂贵的上下文切换:在 Python 中维持数千个并发连接,其上下文切换的开销远高于编译型语言。

Go 的降维打击——数据背后的技术真相

Bifrost 团队最终选择了 Go。这一决定并非出于对语言的偏好,而是基于冷冰冰的 Benchmark 数据。让我们深入分析他们披露的核心指标。

延迟(Latency):微秒与毫秒的鸿沟

数据对比
* Bifrost (Go): ~11 微秒 (0.011ms) / 请求
* LiteLLM (Python): ~8 毫秒 / 请求

这是一个惊人的 700 倍 差距。

虽然 8 毫秒在人类感知中似乎微不足道,但在高并发架构中,这被称为“开销放大”。

  • 累积效应:在一个复杂的 AI Agent 工作流中,可能涉及几十次 LLM 调用。如果每一层网关都增加 8ms 的延迟,累积起来就是可感知的卡顿。
  • 高负载下的劣化:在 10,000 个并发请求下,Go 引入的总处理时间仅为 110ms,而 Python 方案则产生了惊人的 80 秒总 CPU 时间开销。这意味着 Python 方案需要消耗更多的 CPU 核心来维持同样的响应速度,否则请求就会排队,导致尾部延迟(Tail Latency)飙升。

此外,Go 的 net/http 标准库在处理 HTTP 请求时经过了极致优化。Go 不需要像 Python 那样依赖 ASGI/WSGI 服务器(如 Uvicorn),其原生的 HTTP 处理能力配合 Goroutine,使得每个请求的内存分配和 CPU 周期都降到了最低。

并发模型:Goroutine vs Asyncio

架构对比
* Go: 10,000 个 Goroutines,每个仅占用 ~2KB 栈空间。
* Python: 受限于 OS 线程开销或 Event Loop 的单核瓶颈。

LLM 网关的特殊性在于长连接。LLM 的流式输出可能持续数秒甚至更久。这意味着网关必须同时维护成千上万个活跃连接。

Go 的 GMP(Goroutine-Machine-Processor)调度模型天生适合这种场景。成千上万个 Goroutine 可以复用少量的系统线程,上下文切换由 Go Runtime 在用户态极速完成,几乎不消耗系统内核资源。

相比之下,Python 即使使用了 uvloop,在面对海量并发连接的数据搬运时,其解释器的开销依然是一个沉重的包袱。

内存效率与成本

数据对比
* Go: 内存占用降低 ~68%。
* 生产环境: Go 跑在 t3.medium (2 vCPU, 4GB) 上即可;Python 则需要 t3.xlarge。

对于大规模部署 AI 服务的企业来说,这意味着基础设施成本直接减半。

Python 的动态类型系统和垃圾回收机制导致其对象内存占用较大。而 Go 的结构体布局紧凑,且编译器能进行逃逸分析(Escape Analysis),将大量对象分配在栈上而非堆上,从而显著降低了 GC 压力和内存占用。

社区深度探讨——AI 时代的语言版图重构

这篇帖子在 r/golang 引发了极高质量的讨论,评论区揭示了行业内更深层次的趋势。

“AI 能够写代码”改变了竞争规则

过去,Python 的一大优势是“开发效率高”。写 Python 代码通常比写 Go 或 Rust 快。

但在 2026 年,“Agentic Coding”(即利用 AI Coding Agent 辅助编程)已经成为主流。

有开发者指出:“LLM 让编写 Rust 和 Go 变得非常高效,你完全可以享受到高性能语言的红利,而不用支付编写它们的‘学习成本’。”

这是一个极其深刻的洞察。

  • Rust 的借用检查器:以前是新手的噩梦,现在 LLM 可以很好地处理生命周期标注。
  • Go 的样板代码:if err != nil 虽然繁琐,但 Copilot/Cursor/Claude Code等 可以一键生成。

当“编写代码”不再是瓶颈时,“运行时性能”和“稳定性”的权重就被无限放大了。这进一步削弱了 Python 在后端基础设施层的竞争力。

Rust 还是 Go?

既然要高性能,为什么不直接上 Rust?

评论区对此展开了激辩。虽然 Rust 在理论上拥有比 Go 更高的性能上限和内存安全性(无 GC),但 Go 在“开发效率”与“运行效率”之间找到了完美的平衡点。

  • Rust: 适合构建数据库、搜索引擎内核等对延迟极其敏感且逻辑复杂的底层组件。但 Rust 的“认知负担”依然较重,且编译速度较慢。
  • Go: 提供了 80% 的 Rust 性能,但只有 20% 的开发难度。对于网关、代理这类中间件,Go 的标准库(特别是 net/http)极其成熟,编译速度极快,且自带 GC 能让开发者从内存管理的细节中解脱出来,专注于业务逻辑(如限流、计费)。

对于大多数 AI 网关场景,Go 是性价比最高的选择。

Python 的归宿:模型与胶水

这是否意味着 Python 将被淘汰?绝不。

社区共识非常明确:Python 的护城河在于 ML 生态。

  • 模型训练与微调:PyTorch/JAX 无可替代。
  • 数据科学与探索:Jupyter Notebook 是数据科学家的后花园。
  • 快速原型开发:在验证想法阶段,Python 依然是最快的。

但在生产环境部署(Production Serving)阶段,架构正在发生分离:

  • 控制平面(Control Plane):由 Go/Rust 接管,负责流量调度、鉴权、日志、监控。
  • 数据平面(Data Plane):核心推理引擎(如 vLLM)虽然内部可能有 C++/CUDA 优化,但外层接口仍常由 Python 封装。

Go 在 AI 领域的未来展望

Bifrost 的案例只是冰山一角。我们正在目睹 Go 语言在 AI 领域的“新基建”运动。

静态二进制文件的魅力

Deployment simplicity 是作者提到的另一个关键点。

部署 Python 应用通常意味着:配置 Docker -> 安装 Python -> pip install requirements.txt -> 解决依赖冲突 -> 虚拟环境管理。

而部署 Go 应用:COPY bifrost /usr/local/bin/ -> Run。

在容器化和 K8s 盛行的今天,Go 的静态链接二进制文件极大地简化了 CI/CD 流程,减小了镜像体积,提升了冷启动速度(这对于 Serverless AI 推理尤为重要)。

AI 专有工具链的完善

虽然 Go 在 Tensor 操作库上不如 Python 丰富,但在应用层工具上正在迅速补齐。

  • LangChainGo: 社区正在移植 LangChain 的核心能力。
  • Vector Database Clients: Milvus, Weaviate, Pinecone 等向量数据库都有优秀的 Go SDK。
  • 主流大模型 GenAI SDK: 像Google等主流大模型厂商官方对 Go 的支持力度都很大,Gemini、Claude、OpenAI 等模型的 Go SDK 体验都还不错。

架构师的决策建议

如果你正在构建一个 AI 应用平台:

  • 不要用 Python 写网关:不要让 GIL 成为你高并发路上的绊脚石。
  • 不要用 Go 写模型训练:不要试图挑战 PyTorch 的地位,那是徒劳的。
  • 采用“三明治架构”:
    • 上层:Go 处理高并发 HTTP 请求、WebSocket、SSE。
    • 中层:Go 处理业务逻辑、数据库交互、Redis 缓存。
    • 底层:Python/C++ 容器专门负责模型推理,通过 gRPC 与 Go 层通信。

小结

Bifrost 从 Python 到 Go 的迁移,不仅仅是一次代码重写,更是一次架构理念的升级。它证明了在 AI 浪潮中,基础设施的性能与模型的智能同等重要。

随着 LLM 应用规模的爆发式增长,计算成本和响应延迟将成为企业关注的焦点。Go 语言凭借其高效的并发模型、极低的资源占用和极简的部署体验,正在成为 AI 基础设施层的“事实标准”。

对于 Gopher 而言,这是一个最好的时代。我们不需要成为算法专家,只需要发挥 Go 语言最擅长的能力——构建高性能、高可靠的管道,就能在 AI 时代占据不可或缺的一席之地。

资料链接:https://www.reddit.com/r/golang/comments/1r27pqx/why_we_chose_go_over_python_for_building_an_llm/


你认为 Python 会被“边缘化”吗?

随着 Agentic Coding 的普及,高性能语言的入门门槛正在消失。在你的 AI 实践中,是否也感受到了 Python 在生产部署时的无奈?你认为 Go 在 AI 领域还会攻下哪些阵地?

欢迎在评论区分享你的看法!


还在为“复制粘贴喂AI”而烦恼?我的新专栏 AI原生开发工作流实战 将带你:

  • 告别低效,重塑开发范式
  • 驾驭AI Agent(Claude Code),实现工作流自动化
  • 从“AI使用者”进化为规范驱动开发的“工作流指挥家”

扫描下方二维码,开启你的AI原生开发之旅。


你的Go技能,是否也卡在了“熟练”到“精通”的瓶颈期?

  • 想写出更地道、更健壮的Go代码,却总在细节上踩坑?
  • 渴望提升软件设计能力,驾驭复杂Go项目却缺乏章法?
  • 想打造生产级的Go服务,却在工程化实践中屡屡受挫?

继《Go语言第一课》后,我的《Go语言进阶课》终于在极客时间与大家见面了!

我的全新极客时间专栏 《Tony Bai·Go语言进阶课》就是为这样的你量身打造!30+讲硬核内容,带你夯实语法认知,提升设计思维,锻造工程实践能力,更有实战项目串讲。

目标只有一个:助你完成从“Go熟练工”到“Go专家”的蜕变! 现在就加入,让你的Go技能再上一个新台阶!


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

© 2026, bigwhite. 版权所有.

🔲 ⭐

kube-proxy 异常导致节点上的 Pod 无法访问 Service

1. 问题描述 相关 Pod 1 2 3 4 5 6 kubectl -n istio-system get pod -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES istiod-647c7c9d95-7n7n6 1/1 Running 0 77m 10.244.173.51 docs-ai-a800-4 <none> <none> istiod-647c7c9d95-k6l88 1/1 Running 0 30m 10.244.210.160 ai-a40-2 <none> <none> istiod-647c7c9d95-pj82r 1/1 Running 0 51m 10.244.229.217 docs-ai-a800-2 <none> <none> 相关 Service 1 2 3 4 kubectl -n istio-system get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE istiod ClusterIP 10.99.225.56 <none> 15010/TCP,15012/TCP,443/TCP,15014/TCP 645d 1 2 3 4 kubectl -n istio-system get endpoints NAME ENDPOINTS AGE istiod 10.244.173.51:15012,10.244.210.160:15012,10.244.229.217:15012 + 9 more... 645d Endpoints 与 Pod 的 IP 是一致的。 测试结果 在异常节点
🔲 ⭐

Vultr 的免费 VPS

大概一个月前,Vultr 推出了新的免费套餐,在 Vultr 这个级别的 VPS 服务商,这还是第一个提供此类免费服务。在它之前,只有 Google, Amazon,Oracle 有提供这种免费服务。

目前该计划,还属于早鸟阶段。当然我之后就也申请参加这个免费服务使用。大概几个星期前,我收到了审核通过的email。几天前,我开通了这个免费VPS。申请的链接

此免费服务并没有在所有的数据中心开通,只有有限的几个地区,才能看到这个选项。

Free VPS 的规格如下:

  • 1个虚拟CPU
  • 512MB 内存
  • 10GB磁盘
  • 加上 2 TB 的免费全球流量

在部署这个免费 VPS 的时候,需要在 Vultr 后台,选择 Cloud Compute, Intel Regular Performance。

因为只有 512MB 内存,在我选择的时候,Ubuntu 是没法选择的,于是我选择了 Debian 11 作为操作系统。

在收到的 Email 中,以及 Vultr 后台的账号页面,可以看到免费套餐的时间,暂时是一年,不知道以后会否有延长。而且使用有一些限制,比如同时只能有一个免费的VPS。

vultr-free-tier.jpg

下面是通过一些命令得到的规格。

使用 hwinfo --short

root@ft:~# hwinfo --short
cpu:
                       Intel Xeon Processor (Skylake, IBRS), 2593 MHz
keyboard:
  /dev/input/event0    AT Translated Set 2 keyboard
mouse:
  /dev/input/mice      Adomax QEMU USB Tablet
  /dev/input/mice      VirtualPS/2 VMware VMMouse
  /dev/input/mice      VirtualPS/2 VMware VMMouse
monitor:
                       QEMU Monitor
graphics card:
                       VGA compatible controller
sound:
                       Red Hat QEMU Virtual Machine
storage:
                       Red Hat QEMU Virtual Machine
                       Virtio Storage 0
network:
  enp1s0               Virtio Ethernet Card 0
network interface:
  enp1s0               Ethernet network interface
  lo                   Loopback network interface
disk:
  /dev/vda             Disk
partition:
  /dev/vda1            Partition
cdrom:
  /dev/sr0             QEMU DVD-ROM
usb controller:
                       Red Hat QEMU XHCI Host Controller
bios:
                       BIOS
bridge:
                       Red Hat QEMU PCIe Root port
                       Red Hat QEMU PCIe Root port
                       Red Hat QEMU Virtual Machine
                       Red Hat QEMU PCIe Root port
                       Red Hat QEMU PCIe Root port
                       Red Hat QEMU PCIe Root port
                       Red Hat QEMU PCIe Root port
                       Red Hat QEMU PCIe Root port
                       Red Hat QEMU PCIe Root port
                       Red Hat QEMU PCIe Root port
                       Red Hat QEMU Virtual Machine
                       Red Hat QEMU PCIe Root port
                       Red Hat QEMU PCIe Root port
                       Red Hat QEMU PCIe Root port
                       Red Hat QEMU PCIe Root port
                       Red Hat QEMU PCIe Root port
                       Red Hat QEMU PCIe Root port
                       Red Hat QEMU PCIe Root port
                       Red Hat QEMU PCIe Root port
hub:
                       Linux Foundation 2.0 root hub
                       Linux Foundation 3.0 root hub
memory:
                       Main Memory
unknown:
                       FPU
                       DMA controller
                       PIC
                       Keyboard controller
                       PS/2 Controller
                       Red Hat Virtio block device
                       Red Hat Virtio network device
                       Red Hat QEMU Virtual Machine
                       Red Hat Virtio RNG
                       Red Hat Virtio memory balloon
                       Virtio Unclassified device
                       Virtio Unclassified device
性能就不测了,这种免费的 VPS 也就是能用,性能肯定是不会好的。

目前我实际用在网站上的 VPS 是 High Performance 高性能类型的 VPS。如果你也想尝试 Vultr,请使用本站的推荐链接,可获得100美元抵用券。
logo_mono_onwhite.png

🔲 ⭐

几种静态网站托管平台的 Web 服务访问特性实验对比

最近洒家的 GitHub Pro 教育优惠到期了。洒家担心无法再从私有仓库发布 GitHub Pages 网站,于是提前研究了其他几款主流的静态网站托管平台,发现它们的 Web 服务配置和 GitHub Pages 有一些差别,服务器的行为特性各不相同。于是洒家设计了一套测试样例进行了更深入的实验,简单分析了一下实验结果,供读者在选择托管平台、配置静态网站生成器时参考。

概述

  • 测试时间:2022 年 11 月
  • 测试对象:
  • 用于测试的静态网站的目录结构:
/
|-- 404.html
|-- index.html
|-- test1
|   `-- index.html
|-- test1.html
|-- test2.html
|-- test3
|   `-- test3.html
`-- test4
    `-- index.html
  • 测试方法:部署上述静态网站后,使用各种路径访问,记录服务器的行为,并在不同平台间进行对比
  • 测试样例:
    • 基本测试:一组可以在 file:// 协议、Apache 服务器等最基本的场景正常使用的访问方式
    • 完整测试:所有可能的访问方式

测试结果

测试结果中,200 /index.html 表示 HTTP 状态码为 200,返回了 /index.html 的文件内容;301 --> /test1/ 表示 HTTP 重定向到 /test1/404 (default) 表示状态码为 404,返回了服务器默认错误页面,以此类推。

下列测试结果中,每个表格都会挑选一个测试对象作为标准测试对象,其他测试对象和标准测试对象对比,行为完全相同的测试点用绿色标记,行为不同但基本上可以等价替换的测试点用黄色标记(包括不同的 3xx 状态码跳转到了相同的 URL、都是 404 状态码但是响应消息正文不同等情况),行为完全不同的测试点用红色标记。红色的格子越多,和标准行为的差别越大。标准测试对象本身不做任何彩色标记。

基本测试

下表为基本测试的测试结果,以 Apache 为标准测试对象:

Apache GitHub Pages GitLab Pages Cloudflare Pages Vercel Netlify
/ 200 /index.html 200 /index.html 200 /index.html 200 /index.html 200 /index.html 200 /index.html
/index.html 200 /index.html 200 /index.html 200 /index.html 308 --> / 200 /index.html 200 /index.html
/test1.html 200 /test1.html 200 /test1.html 200 /test1.html 308 --> /test1 200 /test1.html 200 /test1.html
/test1/ 200 /test1/index.html 200 /test1/index.html 200 /test1/index.html 200 /test1/index.html 200 /test1/index.html 301 --> /test1
/test1/index.html 200 /test1/index.html 200 /test1/index.html 200 /test1/index.html 308 --> /test1/ 200 /test1/index.html 200 /test1/index.html
/test2.html 200 /test2.html 200 /test2.html 200 /test2.html 308 --> /test2 200 /test2.html 200 /test2.html
/test3/test3.html 200 /test3/test3.html 200 /test3/test3.html 200 /test3/test3.html 308 --> /test3/test3 200 /test3/test3.html 200 /test3/test3.html
/test4/ 200 /test4/index.html 200 /test4/index.html 200 /test4/index.html 200 /test4/index.html 200 /test4/index.html 200 /test4/index.html
/test4/index.html 200 /test4/index.html 200 /test4/index.html 200 /test4/index.html 308 --> /test4/ 200 /test4/index.html 200 /test4/index.html

完整测试

下列表格展示了完整测试的测试结果。下表以 Apache 为标准测试对象:

Apache GitHub Pages GitLab Pages Cloudflare Pages Vercel Netlify
/ 200 /index.html 200 /index.html 200 /index.html 200 /index.html 200 /index.html 200 /index.html
/index 404 (default) 200 /index.html 200 /index.html 308 --> / 404 /404.html 301 --> /
/index.html 200 /index.html 200 /index.html 200 /index.html 308 --> / 200 /index.html 200 /index.html
/test1 301 --> /test1/ 200 /test1.html 302 --> /test1/ 200 /test1.html 200 /test1/index.html 200 /test1.html
/test1.html 200 /test1.html 200 /test1.html 200 /test1.html 308 --> /test1 200 /test1.html 200 /test1.html
/test1/ 200 /test1/index.html 200 /test1/index.html 200 /test1/index.html 200 /test1/index.html 200 /test1/index.html 301 --> /test1
/test1/index 404 (default) 200 /test1/index.html 200 /test1/index.html 308 --> /test1/ 404 /404.html 301 --> /test1/
/test1/index.html 200 /test1/index.html 200 /test1/index.html 200 /test1/index.html 308 --> /test1/ 200 /test1/index.html 200 /test1/index.html
/test2 404 (default) 200 /test2.html 200 /test2.html 200 /test2.html 404 /404.html 200 /test2.html
/test2/ 404 (default) 404 /404.html 200 /test2.html 308 --> /test2 404 /404.html 301 --> /test2
/test2.html 200 /test2.html 200 /test2.html 200 /test2.html 308 --> /test2 200 /test2.html 200 /test2.html
/test2.html/ 404 (default) 404 /404.html 200 /test2.html 404 /404.html 200 /test2.html 301 --> /test2
/test3 301 --> /test3/ 301 --> /test3/ 302 --> /test3/ 404 /404.html 404 /404.html 404 /404.html
/test3/ 200 (Index of /test3) 404 /404.html 404 /404.html 404 /404.html 404 /404.html 404 /404.html
/test3/test3 404 (default) 200 /test3/test3.html 200 /test3/test3.html 200 /test3/test3.html 404 /404.html 200 /test3/test3.html
/test3/test3.html 200 /test3/test3.html 200 /test3/test3.html 200 /test3/test3.html 308 --> /test3/test3 200 /test3/test3.html 200 /test3/test3.html
/test4 301 --> /test4/ 301 --> /test4/ 302 --> /test4/ 308 --> /test4/ 200 /test4/index.html 301 --> /test4/
/test4/ 200 /test4/index.html 200 /test4/index.html 200 /test4/index.html 200 /test4/index.html 200 /test4/index.html 200 /test4/index.html
/test4/index 404 (default) 200 /test4/index.html 200 /test4/index.html 308 --> /test4/ 404 /404.html 301 --> /test4/
/test4/index.html 200 /test4/index.html 200 /test4/index.html 200 /test4/index.html 308 --> /test4/ 200 /test4/index.html 200 /test4/index.html
/404 404 (default) 200 /404.html 200 /404.html 200 /404.html 404 /404.html 200 /404.html
/404.html 200 /404.html 200 /404.html 200 /404.html 308 --> /404 200 /404.html 200 /404.html
/xx 404 (default) 404 /404.html 404 /404.html 404 /404.html 404 /404.html 404 /404.html
/xx.html 404 (default) 404 /404.html 404 /404.html 404 /404.html 404 /404.html 404 /404.html
/xx/ 404 (default) 404 /404.html 404 /404.html 404 /404.html 404 /404.html 404 /404.html
/xx/xxx 404 (default) 404 /404.html 404 /404.html 404 /404.html 404 /404.html 404 /404.html
/xx/xxx.html 404 (default) 404 /404.html 404 /404.html 404 /404.html 404 /404.html 404 /404.html
/test1/xx.html 404 (default) 404 /404.html 404 /404.html 404 /404.html 404 /404.html 404 /404.html

下表以 GitHub Pages 为标准测试对象:

Apache GitHub Pages GitLab Pages Cloudflare Pages Vercel Netlify
/ 200 /index.html 200 /index.html 200 /index.html 200 /index.html 200 /index.html 200 /index.html
/index 404 (default) 200 /index.html 200 /index.html 308 --> / 404 /404.html 301 --> /
/index.html 200 /index.html 200 /index.html 200 /index.html 308 --> / 200 /index.html 200 /index.html
/test1 301 --> /test1/ 200 /test1.html 302 --> /test1/ 200 /test1.html 200 /test1/index.html 200 /test1.html
/test1.html 200 /test1.html 200 /test1.html 200 /test1.html 308 --> /test1 200 /test1.html 200 /test1.html
/test1/ 200 /test1/index.html 200 /test1/index.html 200 /test1/index.html 200 /test1/index.html 200 /test1/index.html 301 --> /test1
/test1/index 404 (default) 200 /test1/index.html 200 /test1/index.html 308 --> /test1/ 404 /404.html 301 --> /test1/
/test1/index.html 200 /test1/index.html 200 /test1/index.html 200 /test1/index.html 308 --> /test1/ 200 /test1/index.html 200 /test1/index.html
/test2 404 (default) 200 /test2.html 200 /test2.html 200 /test2.html 404 /404.html 200 /test2.html
/test2/ 404 (default) 404 /404.html 200 /test2.html 308 --> /test2 404 /404.html 301 --> /test2
/test2.html 200 /test2.html 200 /test2.html 200 /test2.html 308 --> /test2 200 /test2.html 200 /test2.html
/test2.html/ 404 (default) 404 /404.html 200 /test2.html 404 /404.html 200 /test2.html 301 --> /test2
/test3 301 --> /test3/ 301 --> /test3/ 302 --> /test3/ 404 /404.html 404 /404.html 404 /404.html
/test3/ 200 (Index of /test3) 404 /404.html 404 /404.html 404 /404.html 404 /404.html 404 /404.html
/test3/test3 404 (default) 200 /test3/test3.html 200 /test3/test3.html 200 /test3/test3.html 404 /404.html 200 /test3/test3.html
/test3/test3.html 200 /test3/test3.html 200 /test3/test3.html 200 /test3/test3.html 308 --> /test3/test3 200 /test3/test3.html 200 /test3/test3.html
/test4 301 --> /test4/ 301 --> /test4/ 302 --> /test4/ 308 --> /test4/ 200 /test4/index.html 301 --> /test4/
/test4/ 200 /test4/index.html 200 /test4/index.html 200 /test4/index.html 200 /test4/index.html 200 /test4/index.html 200 /test4/index.html
/test4/index 404 (default) 200 /test4/index.html 200 /test4/index.html 308 --> /test4/ 404 /404.html 301 --> /test4/
/test4/index.html 200 /test4/index.html 200 /test4/index.html 200 /test4/index.html 308 --> /test4/ 200 /test4/index.html 200 /test4/index.html
/404 404 (default) 200 /404.html 200 /404.html 200 /404.html 404 /404.html 200 /404.html
/404.html 200 /404.html 200 /404.html 200 /404.html 308 --> /404 200 /404.html 200 /404.html
/xx 404 (default) 404 /404.html 404 /404.html 404 /404.html 404 /404.html 404 /404.html
/xx.html 404 (default) 404 /404.html 404 /404.html 404 /404.html 404 /404.html 404 /404.html
/xx/ 404 (default) 404 /404.html 404 /404.html 404 /404.html 404 /404.html 404 /404.html
/xx/xxx 404 (default) 404 /404.html 404 /404.html 404 /404.html 404 /404.html 404 /404.html
/xx/xxx.html 404 (default) 404 /404.html 404 /404.html 404 /404.html 404 /404.html 404 /404.html
/test1/xx.html 404 (default) 404 /404.html 404 /404.html 404 /404.html 404 /404.html 404 /404.html

结论

在最普通的 Web 服务的基础上,各个平台都加上了一些新的访问和跳转规则。测试结果反映出各个平台实现特性的逻辑和规则的优先级有一些区别(例如 /test1 既可以返回 /test1.html,又可以跳转到 /test1/,还可以直接返回 /test1/index.html)。洒家横竖看了半天,发现想做个简单易懂且全面的总结并不简单,无论用文字还是伪代码都只会越搞越乱,因此只能粗略总结一下,建议有兴趣的读者直接看原始结果。

各个静态网站托管平台的特性支持情况如下:

  • 列目录:全部不支持,访问 /test3/ 都会返回 404
  • 自定义 404 页面 /404.html:全部都支持
  • 省略 .html 访问 .html 文件:除 Vercel 外,全部都支持
  • 访问 /test4 直接访问 /test4/index.html:仅 Vercel 支持,其他都是先跳转到 /test4/

在 Cloudflare Pages 平台,访问所有 /path/to/index.html 都会 308 跳转到 /path/to/,访问其他 .html 文件也会通过 308 跳转去掉 .html 扩展名。根据洒家的理念,静态网站应该保持最大的兼容性,而不依赖任何平台的特性。无论把代码上传到任何平台、任何服务器上都应该可以正常访问,甚至直接在本地通过 file:// 协议打开也应该能正常访问。由于这种强制跳转特性,洒家不会选择 Cloudflare Pages 平台。

而 Netlify 平台在基本测试中也存在瑕疵。同时存在 /test1/index.html/test1.html 的情况下,访问 /test1/ 会先 301 跳转到 /test1,然后 200 返回 /test1.html 的内容,这和一般的预期不同。访问 /test1 的结果可以存在争议,而访问 /test1/ 则应该是想访问 /test1/index.html。再观察 /test2/ 的访问结果,在存在 /test2.html 的情况下,访问 /test2/301 跳转到 /test2,猜测访问 /test1/ 出现的现象可能是由于这个特性的优先级过高。

在 Vercel 平台,存在 /test4/index.html 的情况下,访问 /test4 没有跳转到 /test4/,而是直接 200 返回了 /test4/index.html。同时,我们又可以直接访问 /test4/index.html。同时存在两种路径层级的访问方式,这可能导致页面资源相对引用混乱。

从基本测试可以看出,只有 GitHub Pages、GitLab Pages 和 Vercel 符合洒家的基本标准。从完整测试可以看出,最接近 Apache 的是 Vercel,最接近 GitHub Pages 的则是 GitLab Pages。因此,洒家可能会用 GitLab Pages 或者 Vercel 替代 GitHub Pages。然而到最后洒家忙活的这一圈也没用上,因为洒家另外搞出了一套方案,改用 GitHub Actions 从公开仓库私密发布 GitHub Pages 网站,不用再换平台了。感兴趣的读者可以前往仓库 Phuker/phuker.github.io 查看这套方案。

❌