Go 终于能用 SIMD:archsimd 与可移植 simd 包
来源:internals-for-interns.com — 2026-10
📋 概述
Go 里最普通的逐元素数组加法,CPU 其实一次能算八对,但纯 Go 循环永远用不上这些硬件,因为以前想用 SIMD 只能手写汇编。Go 1.26 引入实验性的 simd/archsimd 包,Go 1.27 又在它之上提供可移植的 simd 包。作者以 Go 1.27.0 加 GOEXPERIMENT=simd 为例,从向量寄存器与 lane 讲起,把比较、掩码、blend、permute、水平加法、尾部处理一路讲到编译器如何把方法调用变成一条 VADDPS 指令。
🔑 核心要点
- Go 1.26 的 simd/archsimd 提供具体宽度的类型(Float32x8 等),Go 1.27 的 simd 在其上做可移植封装
- 向量寄存器按 lane 切分:256 位可装 8 个 float32、4 个 float64 或 32 个 int8,怎么切由具体指令决定
- SIMD 版的 if/else 是 blend:两个结果都算出来,再用 mask 逐 lane 选;与零 blend 就是常见的 masking
- permute 用索引向量决定每个 lane 取谁,可以重复取同一个 lane,这正是快速 JSON 解析器的秘诀
- 尾部用 LoadFloat32x8Part 部分加载,不存在的 lane 补零;作者提醒 Go 1.27.0 的 amd64 实现底下仍会读满 32 字节,上游已修
- x.Add(y) 没有函数体,是编译器 intrinsic:查表变成 SSA 操作,重写规则生成 VADDPS,再由寄存器分配器分配 X/Y 寄存器
- 可移植包的做法是一份代码编译多份:生成 dot@simd128/256/512 与纯 Go 回退,启动时按 CPU 选,GODEBUG=simd=256 可强制宽度
💡 金句
The funny thing is that the same CPU has hardware that could handle eight pairs at once, and a plain Go loop never uses it.
👍 0
👎 0
← 返回 Lobsters 首页