新機能実験的
アーキテクチャ・ベクタ幅非依存のポータブルSIMD APIを提供する simd パッケージが GOEXPERIMENT=simd の下で追加された
New experimental simd package
この項目の注釈は AI により生成されており、誤りを含む場合があります。
使用例のコンパイル検証: 検証済み
概要
Go 1.27 では、実験的な simd パッケージが追加された。ポータブルでベクタ幅に依存しない SIMD (Single Instruction, Multiple Data) サポートを提供し、ビルド時の環境変数 GOEXPERIMENT=simd を設定することで有効になる。
simd パッケージは全アーキテクチャで利用可能で、Int8s や Float32s のようにサイズを指定しないベクタ型を提供する。これらは、simd/archsimd パッケージが持つアーキテクチャ固有・ハードウェア対応の操作や、容易にエミュレートできる操作の「スケーラブルな部分集合」をサポートする。ハードウェア命令が利用可能であればそれを使用し、利用できない環境ではソフトウェアエミュレーションにフォールバックする。
導入経緯
本提案は #73787 で説明された「二層アプローチ」の上位層(ポータブルAPI)にあたる最初の実装であり、Go 1.26 で導入されたアーキテクチャ固有の下位API simd/archsimd の上に構築される。提案は #78902 で議論された。
議論タイムライン
- 2026-05-06: Proposal Review Meeting で議論され、「active」に。広範な反対意見はなく、API詳細についての質問が中心だった。
- 2026-05-13: 「likely accept」に変更。
GOEXPERIMENTによる限定公開であり将来の変更を前提とした導入であることが判断材料となった。 - 2026-05-21: 前回からの追加のコンセンサス変更がなかったため、正式に「accepted」として承認された。
議論のハイライト
- 固定ベクタ幅(例えば
Uint32x8のように要素数を固定した型)のサポートは、ハードウェアより広い幅をエミュレートする実装コストが大きいことから、現バージョンではスコープ外とされた。 simd/archsimd間の相互変換方式として、ToArch() anyを型スイッチで処理する案(Option 1)と、ジェネリクスを用いる案(Option 2b)が検討された。ジェネリックメソッドが型チェッカのクラッシュを引き起こしたため、Option 1 が暫定的に採用され、FromArchはレーン数が期待と異なる場合に panic する方針となった。unsafe.Sizeof(simd.Int32s{})はスケーラブルなアーキテクチャではコンパイル時に決定できないため、型パラメータのSizeofと同様に非定数として扱う方向で合意した。これに伴い言語仕様(go.dev/ref/spec#Package_unsafe)の更新が必要になる点も指摘された。- メモリ上のレイアウトとエンディアンについては、スライス要素0をベクタ要素0に対応させるリトルエンディアン順を標準とし、ビッグエンディアンの s390x では単一命令のパーミュテーションで対応する方針が示された。
- レビュー会議で指摘された
LoadMask<Size>Slice系メソッドの存在はAPI生成ツールの不具合であり、プロトタイプ側で既に修正済みであることが確認された。
使用例
Before
package main
import "fmt"
func sumInt8(a, b []int8) []int8 {
out := make([]int8, len(a))
for i := range a {
out[i] = a[i] + b[i]
}
return out
}
func main() {
a := []int8{1, 2, 3, 4}
b := []int8{10, 20, 30, 40}
fmt.Println(sumInt8(a, b))
}
After
//go:build goexperiment.simd
package main
import (
"fmt"
"simd"
)
func sumInt8(a, b []int8) []int8 {
va, _ := simd.LoadInt8sPart(a)
vb, _ := simd.LoadInt8sPart(b)
vr := va.Add(vb)
out := make([]int8, vr.Len())
vr.StorePart(out)
return out[:len(a)]
}
func main() {
a := []int8{1, 2, 3, 4}
b := []int8{10, 20, 30, 40}
fmt.Println(sumInt8(a, b))
}
移行時の注意
- 現時点では
GOEXPERIMENT=simdを指定してビルドした場合のみ利用可能な実験的機能であり、将来のリリースでAPIが変更される可能性がある。 unsafe.Sizeof(simd.Int32s{})などスケーラブル型を含む型のサイズはコンパイル時定数にならない予定であり、定数式が前提のコードでは注意が必要。- reduction(集約)演算のようにハードウェアが直接サポートしない操作は現行APIに含まれていない。必要な場合は
ToArch()でアーキテクチャ固有型に降格するか、Store/StorePartでスライスに書き出してから通常のループで集約する必要がある。 - 固定ベクタ幅(例えば8要素固定の
Uint32x8相当)を移植可能な形で扱いたい場合、現バージョンのAPIではサポートされていない。
実装解説
src/simd/doc.goはパッケージ全体に//go:build goexperiment.simdを付与しており、simdパッケージ自体がこのビルドタグでゲートされていることが分かる。同ファイルのBUGコメント(l=89)には、リフレクション経由の呼び出しが動作しないこと、SIMD依存のグローバル変数初期化が動作しないこと、内部で書き換えられた名前がスタックトレースやデバッガに現れうることが明記されている。- 各ベクタ型は
src/simd/simd_types.goで_simd bridge.ZeroSizedマーカーフィールドとa, b uint64のプレースホルダを持つ構造体として定義されている(コメントの通り実際のベクタ幅はこれより大きくなりうる)。このファイルはsimd/archsimd/_gen/midwayによって自動生成されたものである。 - 実行時の挙動は
src/simd/midway_common.goのinitが制御しており、GODEBUG=simd設定でエミュレーション/実ハードウェアの切り替えやベクタ長(128/256/512)の指定ができる。ハードウェアが機能を全てサポートしない場合は自動的に狭い幅やエミュレーションへ降格し、+を先頭に付けることでその機能チェックを上書きできる。同ファイルはさらにVectorBitSize()・Emulated()・HasHardwareCarrylessMultiply()という現在の実行環境を調べるための関数を公開している(l=120)。