後端工坊 2026 年 10 月 5 日

2026-10-05 — Go 1.27 新增 archsimd,用 SIMD 不必寫組語

primary=https://go.dev/blog/archsimd

Go 1.27 新增 archsimd,用 SIMD 不必寫組語

Go Blog(Junyang Shao、David Chase)· 2026-10-02

Go 想用 SIMD,過去只有一條路:手寫組語。現在標準函式庫多了 simd/archsimd 套件,讓向量運算直接寫成 Go 程式碼,由編譯器產生指令。

這個套件在 Go 1.27 以 GOEXPERIMENT=simd 啟用,沿用先前 Go 1.26 的實驗版並改進型別轉換 API。目前支援 amd64、arm64 與 wasm。

原本的問題

官方文章直言,想在 Go 用 SIMD 的人必須忍受寫組語的摩擦。組語函式無法被內聯,也沒有型別檢查,每個架構都要各寫一份。結果是多數人乾脆放棄向量化,或是把熱路徑丟給 cgo。

核心設計

archsimd 的原則是不照搬硬體指令名稱。C 的 _mm512_slli_epi64 在這裡叫 ShiftAllLeft;向量以獨立的 struct 型別表示形狀與元素型別,例如 Float32x4、Int32x8、Uint8x16。遮罩則是不透明的 Mask32x4 這類型別。

同樣語意的方法在不同架構上長得一樣,例如 x.Add(y) 一律是逐元素相加。載入與儲存預設吃 slice:

  • LoadFloat32x4(s []float32) 與 x.Store(s)
  • LoadFloat32x4Part(s) 回傳 (Float32x4, int),不足的 lane 補零
  • LoadFloat32x4Array(*[4]float32) 與 BroadcastFloat32x4(v)

型別重新解讀改用可組合的轉換:ToBits() 把有號整數或浮點轉成無號,ReshapeToUint<W>s() 改變同一暫存器內的元素寬度。像 x.ReshapeToUint32s().BitsToFloat32() 的位元重解讀,官方說執行期成本為零。

實際寫法

文章的範例是用 AVX-512 GFNI 反轉位元組內的位元,先檢查 CPU 特性,不支援就退回純 Go 版本:

func ReverseBits(dst, src []uint8) {
    if !archsimd.X86.AVX512GFNI() {
        slowReverseBits(dst, src)
        return
    }
    revMatrix := archsimd.BroadcastUint64x8(0x8040201008040201)
    var v archsimd.Uint8x64
    for i := 0; i < len(src)-v.Len()+1; i += v.Len() {
        v = archsimd.LoadUint8x64(src[i : i+v.Len()])
        v.GaloisFieldAffineTransform(revMatrix, 0).Store(dst[i : i+v.Len()])
    }
}

影響範圍

執行期特性檢查是必要的。省略檢查,在不支援的硬體上會得到 SIGILL;檢查本身也是給編譯器的提示,讓它能把 AVX-512 的遮罩操作合併成單一指令。部署到混合機型(例如 CI 跑在較舊 CPU 上)的服務,要確認每條 SIMD 路徑都有 fallback。

效能上有三個要點,官方文章明確點出:

  • 迴圈條件寫成 i < len(src)-v.Len()+1,才能消除邊界檢查;寫成 i + v.Len() <= len(src) 則可能溢位。
  • 不要把向量包進 struct 或陣列傳遞,8 個向量參數就直接當參數傳。大型複合型別會溢出到堆疊,而不是留在暫存器,相關追蹤見 #24416。

要試用的人,用 GOEXPERIMENT=simd go test simd/archsimd/... 跑套件測試;跨架構可加上 GOOS=wasip1 GOARCH=wasm 或 GOARCH=amd64。

尚未涵蓋

矩陣擴充(AMX、SME)還不支援,表示法「尚未定案」。SVE、RVV 這類可變長度向量會使用不同的 struct 型別。arm64 的 SVE 與 SVE2 文章稱為「已在進行中」;Go 1.28 之後預計補上寬度無關的型別(如 archsimd.Float32s),並擴及 riscv64、ppc64、s390x、loong64。官方未公布 Go 1.28 的時程。

這個套件是架構專屬的:amd64 的程式碼不會自動在 arm64 上跑。文章另有一篇跨平台抽象的姊妹文章(Platform-independent SIMD in Go),不在本文範圍。

原始來源:Arch-specific SIMD in Go、Go issue #24416、Go issue #73787


End of article
0
Would love your thoughts, please comment.x
()
x