标签 编译器 下的文章

告别手写汇编:Go官方提出原生SIMD支持,高性能计算将迎来巨变

本文永久链接 – https://tonybai.com/2025/06/09/go-simd-intrinsics

大家好,我是Tony Bai。

长期以来,在Go语言中追求极致性能的开发者,当遇到需要利用现代 CPU 的 SIMD (Single Instruction, Multiple Data) 能力时,往往不得不求助于手写汇编。这种方式不仅编写和维护困难,还会导致异步抢占失效、阻碍编译器内联优化等问题。现在,这一“不得不”的时代有望终结。 Go 官方团队正式提出了 #73787 提案:在 GOEXPERIMENT 标志下引入架构特定的 SIMD 内置函数。这一里程碑式的提案,旨在为 Go 开发者提供一种无需编写汇编即可利用底层硬件加速能力的方式,预示着 Go 在高性能计算领域将迎来一场深刻的巨变。在这篇文章中,我就和大家一起解读一下这个里程碑式的提案。

两步走战略:从架构特定到可移植 Highway

Go 语言的 API 设计一向以简洁和可移植性著称,但 SIMD 操作的本质却是硬件特定且复杂的。不同 CPU 架构(如 amd64, arm64, riscv64 等)支持不同的向量长度、操作指令甚至数据表示方式。如何在高层抽象的简洁性与底层硬件的复杂性之间找到平衡,是 Go SIMD 设计面临的核心挑战。

为此,Go 团队提出了一个清晰的“两步走”战略:

  1. 第一步:低级、架构特定的 API 与内置函数 (Low-level, architecture-specific API)

    • 目标: 提供一组与机器指令紧密对应的底层 SIMD 操作。这些操作将作为 Go 编译器可识别的内置函数 (intrinsics),在编译时直接转换为高效的单条机器指令。
    • 定位: 类似于 syscall 包。它为追求极致性能的“高级用户”提供了直接访问硬件特性的能力,是构建上层抽象的基石。
    • 实现方式: 初期将以 GOEXPERIMENT=simd 的形式提供预览,首先聚焦于 amd64 等架构的定长向量支持。
  2. 第二步:高级、可移植的向量 API (High-level, portable vector API)

    • 目标: 借鉴 C++ Highway 等项目的成功经验,在底层内置函数的基础上,构建一套跨平台、易于使用的高级 SIMD API。
    • 定位: 类似于 os 包。大多数数据处理、AI 基础设施等场景的开发者可以直接使用这个可移植的 API,在不同架构上都能获得良好的性能。

这个分层设计,既满足了对底层硬件极致控制的需求,也为广大开发者提供了简单易用的可移植方案,实现了优雅的权衡。

底层 API 设计哲学与核心要素

提案详细阐述了底层 SIMD API 的设计原则和关键组成部分:

向量类型 (Vector Types)

SIMD 向量类型将被定义为不透明的结构体(Opaque Structs),而非数组,以避免动态索引(硬件通常不支持)带来的问题。类型命名将直观反映元素类型和数量。

package simd

// 示例:在支持的架构上定义
type Uint32x4 struct { a0, a1, a2, a3 uint32 } // 128-bit vector
type Float64x8 struct { /* 8 float64 fields */ } // 512-bit vector

编译器会特殊处理这些类型,确保它们在传递和存储时使用向量寄存器。

操作 (Operations)

向量操作将以方法 (methods) 的形式定义在向量类型上,编译器会将其识别为内置函数。

// Add 每个元素相加
//
// 等价于 x86 指令 VPADDD
func (Uint32x4) Add(Uint32x4) Uint32x4
  • 命名: 采用易于理解的描述性名称(如 Add, Mul, ShiftLeftConst),而非与特定架构指令(如 VPADDD)绑定。不过,注释中会标明对应的机器指令,方便专家查阅。
  • 尽力而为的可移植性 (Best-effort portability): 对于多平台都支持的常见操作,将使用相同的名称和签名。但该层 API 不追求完全的可移植性,通常不会模拟硬件不支持的操作。

加载与存储 (Load & Store)

加载和存储操作将通过函数实现,通常接受指向固定大小数组的指针。为了方便,也会提供从切片加载的辅助函数。

// 从指向数组的指针加载
func LoadUint32x4(p *[4]uint32) Uint32x4

// 从切片加载
func LoadUint32x4FromSlice(s []uint32) Uint32x4 {
    return LoadUint32x4((*[4]uint32)(s))
}

// 存储到指向数组的指针
func (v Uint32x4) Store(p *[4]uint32)

掩码类型 (Mask Types)

不同架构对掩码的表示方式差异巨大(如 AVX512 的 k-register vs AVX2 的向量寄存器)。为屏蔽这种复杂性,掩码将表示为不透明类型(如 Mask32x4)。编译器会根据上下文选择最高效的硬件表示。

// 比较操作返回掩码
func (Uint32x4) Equal(Uint32x4) Mask32x4 

// 带掩码的加法 (仅对掩码为 true 的元素进行操作)
func (Uint32x4) AddMasked(Uint32x4, Mask32x4) Uint32x4

// 掩码可以与向量互相转换
func (Mask32x4) AsVector() Int32x4

API 组织模式的探讨

除了提案本身,Go团队成员@dr2chase 的示例项目 go_simd_examples 进一步探讨了 SIMD 包的不同组织模式,这对于我们理解未来 API 的可能形态至关重要。

  • 模式 A:单一 simd 包 (提案当前倾向)

    • 所有向量类型和操作都在一个 simd 包内,通过构建标签(build tags)为不同架构提供实现。
    • 开发者通过运行时检查(如 simd.BitLen(), simd.Scalable())来调度不同向量长度(128/256/512位)或可伸缩向量的实现。
    • 优点: 用户只需导入一个包,API 表面上看起来是统一的。
    • 挑战: 需要开发者编写运行时分派逻辑,且代码可移植性依赖于“尽力而为”的公共 API 子集。有开发者指出,这使得在无 build tag 的通用文件中编写 SIMD 代码变得困难,因为 simd 包本身可能在某些架构上不存在。
  • 模式 B:每个架构一个 simd 子包 (simd_amd64, simd_arm64等)

    • 每个架构的 SIMD 内置函数被隔离在各自的包中。开发者通过 build tag 和不同的导入语句来使用特定于架构的功能。
    • 优点: 借鉴了 syscall 包拆分的经验,API 边界清晰,明确了代码的非可移植性。文档和工具(如 gopls)能更好地为特定架构提供支持。
    • 挑战: 对于共享相同算法逻辑但仅向量类型不同的代码,会导致更多的代码重复。
  • 模式 C:每个向量长度一个 simd 子包 (simd_128, simd_256, simd_s等)

    • 这是一种更激进的探索,将 API 按向量能力(长度)划分。
    • 优点:
      • 允许在包级别定义常量(如 simd_128.NFloat64s),减少了代码中的硬编码。
      • 可以通过统一的类型后缀(如 simd_256.Float64s)来指代该包内最大长度的向量,使得为不同向量长度编写的代码在结构上更相似,更接近可伸缩向量的写法。
      • 对于 amd64 架构,这种方式能更清晰地区分不同指令集下的同尺寸向量操作(例如,simd_128 包中的操作对应 SSE,而 simd_256 包中128位操作则使用 AVX 指令)。
    • 挑战: 增加了包的数量,开发者需要根据目标硬件能力选择导入正确的包。

@dr2chase 的示例通过一个“加权内积”的例子,分别用这三种模式实现了跨架构的 SIMD 加速,直观地展示了不同组织方式对代码结构和可维护性的影响。

社区反馈与深入讨论

73787提案引发了社区专家的热烈讨论,一些关键点包括:

  • API 命名哲学 (Add vs. VPADDD): ianlancetaylor 认为,使用特定于架构的指令名或 C/C++ 内置函数名,对专家更友好,便于他们直接将在其他平台的经验移植过来。而 cherrymui则认为,描述性的通用名称(如 Add)对代码的读者更友好,因为大多数人不是 SIMD 专家,通用名称降低了理解门槛。最终提案倾向于后者,并通过注释标明具体指令来服务专家。
  • 处理立即数操作数: 对于需要编译时常量的指令(如 VPINSRD),提案建议开发者传入常量。如果传入变量,编译器可能会回退到效率较低的模拟实现或表驱动跳转。
  • 每架构一个包的呼声: 有一部分开发者强烈建议采用类似 syscall 分拆的模式,即每个架构一个独立的 simd 包。他们认为这能更清晰地界定可移植性边界,避免一个看似统一的 simd 包在不同平台下行为不一所带来的困惑。
  • 对非原生数据类型的支持: 提案确认了未来支持如 bfloat16、float16 等 Go 语言本身没有原生标量类型的计划,这些类型将仅以向量形式存在于 simd 包中。
  • 与现有工具链的整合: 讨论涉及了与 golang.org/x/sys/cpu 的集成、GOAMD64 等环境变量的影响、VZEROUPPER 指令的自动插入、以及编译器内联启发式算法的改进等深度技术问题。

小结

Go 官方的 #73787 SIMD 提案,标志着 Go 语言在拥抱底层硬件能力、提升高性能计算方面迈出了决定性的一步。其“两步走”战略清晰地规划了从架构特定的底层能力到高级可移植 API 的演进路径,既务实又富有远见。

对 Go 开发者而言,这意味着:

  • 性能优化的新途径: 未来,我们将能用纯 Go 代码(而非汇编)来编写利用 SIMD 的高性能计算密集型任务,如数据处理、加密、多媒体编解码、AI/ML 等。
  • 更低的入门门槛: 相比于手写汇编,基于 Go 方法和类型的 SIMD API 将极大地降低学习和使用门槛。
  • 持续关注实验性特性: 该功能将首先通过 GOEXPERIMENT=simd 标志发布,这为社区提供了宝贵的早期试用和反馈机会,共同塑造其最终形态。

虽然关于 API 的组织形式、命名约定等细节仍在积极讨论中,但提案所确立的大方向——通过编译器内置函数提供底层支持,并在此基础上构建高级抽象——已经非常明确。这不仅将直接惠及需要极致性能的 Go 应用,也将为 Go 语言的整体生态(例如标准库的内部优化)注入新的活力。

从提案目前的状态来看,最早也要等到Go 1.26版本落地了。


微专栏推荐:征服 Go 并发测试

想彻底告别并发测试的“噩梦”吗?我的全新微专栏 《征服 Go 并发测试》(共三篇)现已上线!

本系列深入剖析并发测试痛点、testing/synctest 的设计原理与 API,并提供丰富的实战案例。助你轻松驾驭并发测试,写出更稳健的 Go 应用!

扫码订阅,即刻解锁并发测试新境界!

更多微专栏,敬请期待! 对后续选题(如 Go 性能优化、AI 与 Go 结合等)有何期待或建议?欢迎在留言区畅所欲言,一起打造更精彩的内容!


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

AI 编码工具“真香”还是“智商税”?一位资深码农的“挑衅”与Go开发者的反思

本文永久链接 – https://tonybai.com/2025/06/03/provocation-about-ai-assisted-programming

大家好,我是Tony Bai。

最近,fly.io 博客上该公司开发者 Thomas Ptacek 的一篇题为《My AI Skeptic Friends Are All Nuts》的文章,在开发者社区掀起了不小的波澜,一度登顶HN。Ptacek 以一位自称“严肃开发者”(从C语言到Go、Rust均有涉猎)的口吻,向那些对 AI 辅助编程持怀疑态度的“聪明朋友们”发出了略带“挑衅”的宣言:“即使 LLM 今天停止所有进展,它仍然是我职业生涯中发生的第二重要的事情”

这篇文章的观点之鲜明、论证之犀利,让我印象深刻。恰逢 前期Google I/O 2025 大会再次展示了 Gemini 等 AI 模型在编码领域的惊人进展,我们不禁要问:AI 编码工具,究竟是能极大提升生产力的“真香”利器,还是又一轮被过度炒作的“智商税”?作为开发者,特别是 Gopher,我们又该如何看待和应对这场正在发生的变革?

在这篇文章中,我就和大家一起来看看 Thomas Ptacek 对AI辅助编程演进的犀利观点以及他的反思。看看你是否认同他的想法。

误区澄清:现代 AI 辅助编程早已不是“复制粘贴”

Ptacek 在文章开篇就点出了一个关键问题:很多人对 AI 辅助编程的印象,还停留在半年前甚至两年前的水平。他写道:“如果你在6个月前(或者,天哪,两年前用Copilot的时候)尝试使用LLM编码并失败了,那么你并没有在做大多数严肃的LLM辅助编码者正在做的事情”。

那么,现在“严肃的LLM辅助编码者”在做什么呢?Ptacek 强调,他们使用的是 Agent (智能体)。这些 AI Agent 不再仅仅是根据提示生成代码片段让你复制粘贴,它们能够:

  • 自主地在你的代码库中进行探索。
  • 直接创建和修改文件。
  • 运行各种工具, 如编译器、测试框架、linter、formatter 等。
  • 与 Git 等版本控制系统交互。
  • 根据编译和测试结果进行迭代和修正。
  • 通过 MCP 或类似机制调用你设置的任意工具。

Ptacek强调:如果你对 AI 编码的印象还停留在 ChatGPT 网页上简单问答然后手动复制代码,那么你可能真的低估了当前 AI Agent 所能达到的自动化和智能化水平。

AI Agent 如何提升编码效率?Ptacek 的“积极案例”

Ptacek 认为,LLM(通过 Agent)能够极大地提升编码效率,主要体现在以下几个方面:

  1. 处理“乏味代码”: LLM 可以编写你需要编写的大部分乏味代码。而大多数项目中的大多数代码都是乏味的。这能让开发者从重复性的工作中解放出来,更快地进入“调整代码并立即看到效果更好”的“黄金时刻 (golden moment)”,获得即时反馈的“多巴胺冲击”。

  2. 克服项目启动的“惯性”: 面对一个新项目,繁琐的初始设置、依赖管理、基础架构搭建等往往令人望而却步。LLM Agent 可以被指示去“搞定这些破事,直接将你带到“事情几乎可以工作”的阶段。

  3. 自动化“苦差事”: 那些你不想做但又必须做的“脏活累活”,比如大规模的单元测试重构,完全可以交给 AI Agent 在虚拟机里折腾几个小时,然后带着一个 PR 回来。这反而会“逼迫”你去做“真正的工作 (real work)”。

回应常见的质疑:Ptacek 的“辩护”

Ptacek 在文章中也针对开发者对 AI 编码的常见质疑进行了犀利的回击,这些回应也为我们思考 Go 语言在 AI 时代的定位提供了新的视角。

  • 关于代码质量与审查责任——“你根本不知道它写的是什么!”

Ptacek强调,开发者始终对合并到 main 分支的代码负责,无论是否使用 LLM。 LLM 生成的代码是“可知的”,你需要阅读它,甚至花时间将其调整为你自己的风格。如果连 LLM 生成的“乏味、重复”的代码都难以理解和消化,那可能是开发人员的“技能问题”。

  • 关于“幻觉 (hallucination)”问题——“它会编造不存在的API!”

Ptacek 认为,对于编程而言,Agent 通过工具链(linting、编译、运行测试)形成的闭环反馈,已经(或多或少地)解决了“幻觉”问题。“如果它们的LLM编造了一个新的函数签名,Agent会看到错误。它们将其反馈给LLM,LLM会说‘哦,是的,我完全是编造的’,然后重试”。这里不能不提到** Go 语言的快速编译特性,使得这种“试错-反馈-修正”的闭环能够非常高效地运转。同时,Go 强大的标准库和清晰的 API 设计,是否也能减少 LLM“编造”API 的概率,或者使其更容易被工具链检测出来。

  • 关于“代码像初级开发者写的”——“质量太差!”

Ptacek 回应:“一个实习生一个月要花20美元吗?因为 Cursor.ai 就是这个价钱”。他认为,高级开发者的职责之一就是让能力稍逊的编码者(无论是人类还是“智能体”)变得高效。使用好 Agent 本身就是一项技能和一项涉及提示、索引和(尤其是)工具链的工程项目。 LLM 只有在你允许的情况下才会产生劣质代码。

  • 关于“不擅长特定语言 (如 Rust)”——“它写不了我的 Rust!”

Ptacek 认为这更多是语言生态和工具链成熟度的问题,而非 LLM 能力的根本缺陷。他特别指出:“我主要用 Go 工作……Go 恰到好处的类型安全、广泛的标准库以及推崇(通常是重复性)惯用法的文化。LLM 在生成 Go 代码方面表现出色。” 想必很多Go开发者也有着与Ptacek相同的感受,这是 Go 语言在 AI 辅助编程时代的一个显著优势! Go 的简洁性、明确性、强大的标准库覆盖、以及社区对代码规范和惯用法的重视(例如 Effective Go),使得 Go 代码的模式相对统一和可预测,这为 LLM 的学习和生成提供了极大的便利。

对“手工艺精神”与“平庸代码”的再思考

Ptacek 对软件开发中的“手工艺精神”和对“平庸代码”的过度排斥也提出了批判。

  • 他认为:专业软件开发者的工作是用代码为人们解决实际问题。在日常工作中,我们不是工匠。过度追求代码的“优雅”而忽视实际产出,可能是“自我安慰的yak-shaving(指做无关紧要的琐事)”。

  • 对于“平庸代码”,他认为:开发者都喜欢对代码自吹自擂。他们担心LLM降低了质量的“天花板”。也许吧。但它们也提高了“地板”。LLM 生成的“平庸但彻底”的代码,可能比人类开发者“抖机灵”但引入缺陷的代码更有价值。

这也引发我们思考:在追求卓越工程的同时,我们是否也应该更务实地看待不同场景下对代码质量的不同要求?LLM 是否能帮助我们更高效地处理那些“允许平庸”但又耗时耗力的部分,从而让我们能将精力投入到真正需要人类智慧和创造力的核心工作中?

Go 开发者如何拥抱 AI Agent 的时代?

Ptacek 的文章,无论你是否完全认同其所有观点,都为我们描绘了一个 AI Agent 深度参与软件开发的未来图景。作为 Gopher,我们应该如何应对?

  1. 更新认知,拥抱变化: 首先要认识到,现代 AI 辅助编程已经远超简单的代码补全。应该主动去了解和体验基于 Agent 的编码工具。
  2. 学习与 AI Agent 高效协作: 掌握提示工程技巧,学会如何清晰地向 Agent表达需求、提供上下文、引导其生成和修改代码。
  3. 发挥 Go 语言的优势: 利用 Go 的简洁性、强大的标准库、快速的编译和测试工具链,为 AI Agent 构建高效的开发和反馈环境。思考如何让 Go 代码对 AI 更“友好”。
  4. 提升自身的核心价值: 将精力更多地投入到 AI 难以替代的领域:复杂系统设计、架构决策、需求理解与抽象、创新思维、以及对 Go 底层原理和并发模型的深刻理解。
  5. 参与构建 Go 的 AI Agent 生态: Go 语言本身非常适合构建 CLI 工具和后端服务。我们是否可以利用 Go 来创建更强大的、针对 Go 开发的 Agent 辅助工具或平台?

小结:保持开放,主动实践,与 AI 共舞

AI 编码工具究竟是“真香”还是“智商税”?或许答案因人而异,也因我们如何使用它而异。但 Thomas Ptacek 的“挑衅”至少提醒我们,不能用静止的眼光看待飞速发展的技术。

AI 辅助编程的浪潮已然到来。对于我们 Gopher 而言,Go 语言的特性使其在这波浪潮中具有独特的优势。与其固守过去的经验和偏见,不如保持开放的心态,主动去实践和探索,让 AI Agent 成为我们提升自身能力、加速项目交付、并最终能专注于更有创造性工作的强大伙伴。

毕竟,正如 Ptacek 所说,当他那些“聪明的怀疑论朋友们”最终接受并开始使用这些工具时,他们将会让编码 Agent 比今天强大得多。

而我们,又怎能置身事外呢?


聊一聊,也帮个忙:

  • 你目前在工作中使用 AI 辅助编程工具(如 Copilot, Cursor.ai, Gemini Code Assist,Trae等)的体验如何?它在哪些方面帮助了你,又有哪些不足?
  • Ptacek 文章中对 AI 编码的哪个观点让你印象最深刻?你同意还是反对?为什么?
  • 你认为 Go 语言在 AI 辅助编程时代,还有哪些可以进一步优化的方向,以更好地与 LLM Agent 结合?

欢迎在评论区留下你的思考和经验。如果你觉得这篇文章提供了一个值得探讨的视角,也请转发给你身边的开发者朋友们,一起参与这场关于 AI 与编程未来的讨论!

想与我进行更深入的 Go 语言、AI 赋能开发与技术趋势交流吗? 欢迎加入我的“Go & AI 精进营”知识星球

img{512x368}

我们星球见!


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

如发现本站页面被黑,比如:挂载广告、挖矿等恶意代码,请朋友们及时联系我。十分感谢! Go语言第一课 Go语言进阶课 Go语言精进之路1 Go语言精进之路2 Go语言第一课 Go语言编程指南
商务合作请联系bigwhite.cn AT aliyun.com

欢迎使用邮件订阅我的博客

输入邮箱订阅本站,只要有新文章发布,就会第一时间发送邮件通知你哦!

这里是 Tony Bai的个人Blog,欢迎访问、订阅和留言! 订阅Feed请点击上面图片

如果您觉得这里的文章对您有帮助,请扫描上方二维码进行捐赠 ,加油后的Tony Bai将会为您呈现更多精彩的文章,谢谢!

如果您希望通过微信捐赠,请用微信客户端扫描下方赞赏码:

如果您希望通过比特币或以太币捐赠,可以扫描下方二维码:

比特币:

以太币:

如果您喜欢通过微信浏览本站内容,可以扫描下方二维码,订阅本站官方微信订阅号“iamtonybai”;点击二维码,可直达本人官方微博主页^_^:
本站Powered by Digital Ocean VPS。
选择Digital Ocean VPS主机,即可获得10美元现金充值,可 免费使用两个月哟! 著名主机提供商Linode 10$优惠码:linode10,在 这里注册即可免费获 得。阿里云推荐码: 1WFZ0V立享9折!


View Tony Bai's profile on LinkedIn
DigitalOcean Referral Badge

文章

评论

  • 正在加载...

分类

标签

归档



View My Stats