本文へ
ハクソク

世界を動かす技術を、日本語で。

Goにおけるプラットフォーム非依存のSIMD

元記事: 原文(go.dev)

概要

Go 1.26と1.27で SIMD操作用の実験的API が導入。 SIMDにより データベクトルへの高速な一括演算 が可能。 従来は Goアセンブリ記述 のみで利用可能だった。 新APIは アーキテクチャ依存と非依存の両方 を提供。 マルチプラットフォーム・可搬性・効率を両立する simdパッケージ の特徴。

GoのSIMDサポートの進化

  • SIMD(Single Instruction Multiple Data) は、現代CPUの ネイティブ機能 であり、ベクトルデータに対し同時に演算を実行可能。
  • 例:float64値のペア8組を 単一命令で加算 する処理。
  • 暗号処理、データ処理、AI など計算集約タスクで大幅な高速化が可能。
  • GoのGreen Teaガベージコレクタ でも、SIMDを利用しメモリスキャンを高速化。
  • これまでGoからSIMDを利用するには Goアセンブリ記述 が必須で、パフォーマンスが極端に重要な場合に限られていた。
  • 多くのソフトウェアで CPUリソースが未活用 のまま残る課題。

Go 1.26/1.27のSIMD API概要

  • Go 1.26 で amd64向けSIMD API を導入。
  • Go 1.27 で arm64(NEON) および wasm 向けAPIも追加。
  • SIMD APIの設計課題は プラットフォームごとの多様性。
    • ベクトルサイズや演算機能、マスク処理方式などが異なる。
    • 例:ベクトルサイズが128〜512ビットで固定のものもあれば、起動時にサイズを問い合わせる必要があるものも存在。
  • archsimdパッケージ としてアーキテクチャ依存APIを提供。

simdパッケージの特徴と設計

  • Go 1.27 では 完全可搬・プラットフォーム非依存 のSIMDインターフェース(simdパッケージ)も実験的導入。
  • C++のHighway を参考に設計。
  • AVX/AVX2/AVX512(amd64)、NEON(arm64)、wasmのSIMD命令 に対応。
  • サポートされていないプラットフォームでは 効率的なエミュレーション を提供。
  • GOEXPERIMENT=simd をビルド時に指定して利用。
  • ベクトル型は simd.Uint8sやsimd.Float32s などの大文字複数形プリミティブ型。
  • ベクトルのロード/ストアはスライスを利用。
    • 例:simd.LoadFloat32s(x[i:i+a.Len()])でスライスからベクトルロード。

simdパッケージの操作例

  • 内積計算関数の実装例 (float32スライス同士の内積)を提供。
  • sum関数 は現状simdで直接サポートされていないが、次期バージョンでReduceSumが追加予定。
  • ベクトル比較演算 は各要素幅に対応したマスク型(例:Mask8s)が生成され、選択やフィルタ処理に利用可能。

simdパッケージのサポート関数一覧(Go 1.27時点)

  • ベクトルロード/ブロードキャスト/ストア/文字列表現
  • 算術演算:加算、減算、乗算、除算、絶対値、平方根、飽和加算/減算、平均、積和演算など
  • 論理演算:AND, OR, XOR, NOT, マスク付き操作
  • 比較演算:等価、不等価、大なり、小なり、以上、以下
  • 型変換:Float/Int/Uint間の変換、マスク変換
  • シフト・ローテート:全ビット左/右シフト・ローテート
  • ゼロコストリシェイプ:ビット列や異なる幅のベクトルへの変換

SIMDアーキテクチャ間の主な差異

  • ベクトルサイズの固定/可変性 (例:wasmは128ビット固定、riscv64は128〜65536ビット可変)
  • マスク処理方式 (例:AVX512は要素ごとにマスクビット、NEONやwasmはビット演算で対応)
  • 命令セットの違い (例:暗号系演算、要素並べ替え、整数比較などのサポート状況)

アーキテクチャ依存SIMDとの連携

  • simdパッケージ型は ToArch()メソッド でアーキテクチャ固有型(any型)に変換可能。
  • 逆変換は simd.FromArch 関数で実施。
  • これにより 足りない機能だけをアーキテクチャ固有実装 で補うことが可能。
  • 完全な例: Int8s.OnesCount() (ビットカウント)のアーキテクチャ依存実装。

まとめ

  • GoのSIMDサポートは大きく進化 し、より多くのアプリケーションで CPUリソースを効率活用 可能に。
  • simdパッケージは可搬性とパフォーマンスを両立 しつつ、アーキテクチャ固有拡張も許容。
  • 今後も APIの拡充とエミュレーション性能向上 が期待される。

提供: ハクソク · 要約更新:

AIによる日本語要約です。内容は原文とあわせてご確認ください。

Hacker Newsの議論を読む

Hackerたちの意見

この機能は、すでにマルチコアで動いているGoプロジェクトの低レベルパフォーマンスを最適化するための多くの扉を開いてくれるね。確か、SIMDやそのバリエーションをサポートしている標準ライブラリを持っている言語はあまり多くないはず。最近のGoの新しい試みが大好きだよ。

└

計算のベクトル化は、Matlabの秘密のソースだったね。

└

普通のCやC++に加えて、Java、.NET、D、Zig、Julia、Swift、Rustもあるから、アセンブリを手動で書く代わりにGoがグループにいるのはありがたいよね。でも、手動でSIMDを書く方法を採用している言語はあまり多くない。ほとんどの人が良いSIMDコードを書く方法を知らないから、私も全然わからないよ。

これ、めっちゃいいね!Goの最大の不満の一つだったから。ほとんどの場合、適切なパフォーマンスを得るためにはC/C++のコードをリンクしなきゃいけなかったから。唯一のマイナス点は、オートベクトル化が「十分良い」ことが多くて、そのギャップにはあまり対処できてないことかな。

└

最初のステップとして、適切な数値ループをSIMDに書き換えるリンターのルールを書くことができるかもしれないね。すでにいくつかのループタイプを書き換えるルールがあるから、実現可能だと思うよ。

└

かわいそうなアセンブラとunsafeパッケージが隅っこに忘れ去られてるね。CGOに手を伸ばすのが簡単な方法だけど、それがGoで使える唯一のツールってわけじゃないよ。

└

参考までに、Goコンパイラのためにすでにかなりのオートベクトル化の作業が進行中だよ。ここにCLスタックがあるよ: https://go.dev/cl/791740 オープンソースプロジェクトだから予測は難しいけど、個人的には何らかの形で実現すると思う(コンパイラのコードの複雑さを大きく増やさず、コンパイル速度を過度に遅くすることなく、すでに良い結果を示しているから)。外部の貢献者が推進していて、過去にもGoコンパイラに良い変更を加えているからね。(オートベクトル化の作業は、彼らの博士課程や他の学術研究の一部かもしれないけど、確かではないよ。)

Goの問題はパフォーマンスじゃなくて、C/C++との相互運用のオーバーヘッドだね。数回のアップデートで「30%オーバーヘッドが少ない」って言われたけど、99%のケースではそれは真実じゃないし、十分じゃないよ。

└

なんでそうなるの?低レベルプログラミングはよく知らないから、GoがCとの相互運用に制限がある理由が分からない。

└

CGO の代わりにアセンブリを使うのはちょっと怖いよね。8ビット時代には、10歳でアセンブリをコーディングしてたし、Spectrum や C64、Atari、Apple、Acorn、MSX でやってたなぁ…。

これが僕がGoを好きな理由だよ。誰もこれを求めてなかったけど、ちゃんと時間をかけてやってくれたし、メモリ安全で高水準なシステム言語としてGoを推し続けてる。

└

Goのデータレースはメモリ安全じゃないよ。

Hacker Newsで議論の続きを見る

同じ出典の記事