後端工坊 2026 年 9 月 25 日

2026-09-25 — Go 新增可攜 simd 套件,向量化免寫平台組合語言

primary=https://go.dev/blog/simd-experiment primary=https://github.com/golang/go/issues/78979

Go 新增可攜 simd 套件,向量化程式碼免寫平台組合語言

The Go Blog · 2026-09-24

Go 的向量化程式碼從此不必為每個 CPU 架構各寫一份組合語言:新的 simd 套件把 Go 1.26 中只支援 amd64 的 simd/archsimd 再往上抽象一層,在 Go 1.27 加入 arm64(NEON)與 wasm 的實作,讓同一段向量運算程式碼在不同平台各自編譯出對應的原生指令。這篇文章由 David Chase 與 Junyang Shao 發表於 Go 官方部落格,目前仍屬 GOEXPERIMENT=simd 下的實驗性 API。

原本的問題

在這之前,Go 裡要寫向量化程式碼,只有兩條路:手寫 Go 組合語言,或是完全不做向量化、依賴編譯器自動向量化(效果通常有限)。手寫組合語言意味著每個目標平台各維護一份實作,amd64 一份、arm64 一份,效能關鍵函式庫的作者因此背上沉重的維護成本。Go 1.26 引入的 simd/archsimd 雖然把 amd64 的 intrinsics 包裝成 Go API,但它是平台專屬的——換到 arm64 或 wasm 上就完全用不了,仍然沒解決「一次撰寫、到處編譯」的問題。

核心改動

新的 simd 套件用「所有目標平台的交集」定義可攜 API:只暴露在 amd64、arm64、wasm 上都能合理實作的運算,缺少對應硬體指令的平台則由編譯器產生高效的模擬程式碼補上空缺。實作上,編譯器在前端對呼叫 simd API 的函式做 AST 重寫,替每個實際使用到的型別產生特化版本(函式名稱會帶上 @simd 加數字的後綴),再由後端依目標平台選擇對應指令;分派(dispatch)的判斷會被提到迴圈外,避免拖慢向量運算本身。

套件也拿掉了固定大小的向量型別,改用 Len() 回傳目前平台的向量寬度,讓同一段程式碼在不同硬體上自動套用不同的向量長度。支援的運算涵蓋載入/儲存、算術(Add、Mul、MulAdd、Div)、比較、布林運算、遮罩、位元操作、型別轉換與資料重排。以內積運算為例,原文給出的寫法是:

func innerProduct(x, y []float32) float32 {
    var a simd.Float32s
    var i int
    for i = 0; i < len(x)-a.Len()+1; i += a.Len() {
        u := simd.LoadFloat32s(x[i : i+a.Len()])
        v := simd.LoadFloat32s(y[i : i+a.Len()])
        a = u.MulAdd(v, a)
    }
    // ... 處理剩餘不足一個向量長度的尾端
}

迴圈本身不出現任何平台判斷,a.Len() 在 amd64 上可能是 8、在 arm64 NEON 上可能是 4,全由編譯器依目標架構決定。文章也提到這種「交集加模擬」的設計思路參考了 C++ 的 Highway 函式庫,差別在於 Go 選擇用編譯器前端的 AST 重寫來產生特化版本,而不是像 C++ 樣板那樣在使用者端展開,維持 Go 一貫「少一點語言機制、多一點編譯器代勞」的風格。

simd/archsimd(Go 1.26)simd(Go 1.27)
支援平台僅 amd64amd64、arm64(NEON)、wasm
向量寬度對應 amd64 暫存器固定型別由 Len() 依平台動態決定
缺指令時不適用(平台專屬)編譯器產生模擬程式碼補齊
適用情境只跑 amd64、要榨乾特定指令集需要同一份程式碼跨平台編譯

影響範圍

直接受影響的是寫效能敏感 Go 函式庫的作者——影像處理、數值運算、編碼/雜湊等原本得手刻組合語言或依賴 cgo 呼叫 C 函式庫的場景,現在可以先評估用 GOEXPERIMENT=simd 開關試跑可攜版 API。要注意的是這個套件與 Go 1.26 的 simd/archsimd 是並存的兩個定位:archsimd 保留給需要壓榨 amd64 特定指令、不在意可攜性的場景,simd 則是犧牲部分極限效能換取跨平台一致性。

API 本身尚未定案。golang/go#78979(cherrymui 於 2026 年 4 月 27 日提出)正在討論把 simd/archsimd 從實驗性轉為預設啟用,其中就列出多項尚未拍板的調整,包括改變 ShiftAll{Left,Right} 的參數型別、替整數向量加上 Neg、替遮罩加上 AndNot、Not、Xor 等便利方法。該 issue 目前狀態為 Proposal-Hold,尚未決議。換句話說,現在跟進 simd 套件的團隊,應該預期後續版本的方法簽章仍可能微調,暫時別把它用在需要長期 API 穩定性的公開介面上。

原始來源:Platform-independent SIMD in Go(The Go Blog)、golang/go#78979


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