目的
GPU付きPCを購入したが、
本当にGPU計算が動くのか
Go言語から使えるのか
CPUと比べてどの部分が速く/遅くなるのか
を、誰でも再現できる形で確認する。
失敗した試行や遠回りはすべて排除し、
**「この順でやれば必ず動く」**内容のみを記載します。
0. 前提条件(必須)
ハードウェア
-
NVIDIA GPU(例:RTX 4060 Laptop GPU)
-
※ AMD / Intel GPU でも理論上可だが、本手順は NVIDIA 前提
-
OS
-
Windows 10 / 11(64bit)
1. NVIDIA ドライバの確認(最重要)
1.1 NVIDIA ドライバが入っているか
PowerShell で確認:
-
GPU名とドライバ情報が表示されればOK
-
表示されなければ、NVIDIA公式サイトから最新ドライバをインストール
2. Go 言語のインストール
2.1 Go をインストール
-
Windows用インストーラ(msi)を実行
-
デフォルト設定でOK
確認:
go version
例:
go version go1.22.x windows/amd64
3. MSYS2 のインストール(OpenCL用)
3.1 MSYS2 をインストール
-
インストール先:
C:\msys64(デフォルト)
3.2 UCRT64 シェルを使用する(重要)
スタートメニューから:
MSYS2 UCRT64
を起動すること。
4. OpenCL 開発環境の導入(UCRT64)
4.1 パッケージ更新
pacman -Syu
(再起動を求められたら UCRT64 を再起動)
4.2 OpenCL 関連パッケージをインストール
pacman -S --needed \
mingw-w64-ucrt-x86_64-opencl-headers \
mingw-w64-ucrt-x86_64-opencl-icd
4.3 OpenCL ライブラリの存在確認(重要)
ls /ucrt64/lib | grep OpenCL
期待される出力:
libOpenCL.dll.a
これが 見えなければ先に進まない。
5. Go から OpenCL を使う準備
5.1 作業ディレクトリ作成
PowerShell(任意の場所):
mkdir go_test
cd go_test
go mod init go_test
5.2 OpenCL Go バインディングを取得
go get github.com/jgillich/go-opencl/cl
6. OpenCL 1.2 指定(最重要ポイント)
問題点
-
OpenCL Headers はデフォルトで OpenCL 3.0
-
go-openclは OpenCL 1.2 前提 -
指定しないとビルドエラーになる
6.1 環境変数を設定(永続)
PowerShell で一度だけ実行:
setx CGO_CFLAGS "-DCL_TARGET_OPENCL_VERSION=120 -DCL_UNORM_INT24=0x10DF -DCL_DEPTH_STENCIL=0x10BE"
重要
-
実行後 VS Code / PowerShell を完全に再起動
確認:
echo $env:CGO_CFLAGS
表示されればOK。
7. VS Code の準備
7.1 VS Code インストール
7.2 拡張機能
-
Go(公式拡張)
8. 動作確認用プログラム(全文)
以下を main.go として保存。
package main
import (
"encoding/binary"
"fmt"
"math"
"math/rand"
"time"
"unsafe"
"github.com/jgillich/go-opencl/cl"
)
const kernelSrc = `
__kernel void vadd(__global float* a, __global float* b, const int n) {
int i = get_global_id(0);
if (i < n) {
a[i] = a[i] + b[i];
}
}
`
func cpuVadd(a, b []float32) {
for i := range a {
a[i] += b[i]
}
}
func f32ToBytes(xs []float32) []byte {
b := make([]byte, 4*len(xs))
for i, v := range xs {
binary.LittleEndian.PutUint32(b[i*4:], math.Float32bits(v))
}
return b
}
func bytesToF32(b []byte) []float32 {
n := len(b) / 4
xs := make([]float32, n)
for i := 0; i < n; i++ {
u := binary.LittleEndian.Uint32(b[i*4:])
xs[i] = math.Float32frombits(u)
}
return xs
}
func main() {
const N = 5_000_000
const CPU_REPEAT = 5
const GPU_REPEAT = 10
a := make([]float32, N)
b := make([]float32, N)
rng := rand.New(rand.NewSource(1))
for i := 0; i < N; i++ {
a[i] = rng.Float32()
b[i] = rng.Float32()
}
// CPU
aCPU := make([]float32, N)
copy(aCPU, a)
cpuVadd(aCPU, b)
t0 := time.Now()
for i := 0; i < CPU_REPEAT; i++ {
cpuVadd(aCPU, b)
}
fmt.Println("CPU avg time:", time.Since(t0)/CPU_REPEAT)
// OpenCL
platforms, _ := cl.GetPlatforms()
devs, _ := platforms[0].GetDevices(cl.DeviceTypeAll)
dev := devs[0]
fmt.Println("Using device:", dev.Name())
ctx, _ := cl.CreateContext([]*cl.Device{dev})
queue, _ := ctx.CreateCommandQueue(dev, 0)
aBytes := f32ToBytes(a)
bBytes := f32ToBytes(b)
tCopy0 := time.Now()
bufA, _ := ctx.CreateBuffer(cl.MemReadWrite|cl.MemCopyHostPtr, aBytes)
bufB, _ := ctx.CreateBuffer(cl.MemReadOnly|cl.MemCopyHostPtr, bBytes)
fmt.Println("GPU copy host->device:", time.Since(tCopy0))
prog, _ := ctx.CreateProgramWithSource([]string{kernelSrc})
prog.BuildProgram([]*cl.Device{dev}, "")
kernel, _ := prog.CreateKernel("vadd")
kernel.SetArgs(bufA, bufB, int32(N))
queue.EnqueueNDRangeKernel(kernel, nil, []int{N}, nil, nil)
queue.Finish()
t1 := time.Now()
for i := 0; i < GPU_REPEAT; i++ {
queue.EnqueueNDRangeKernel(kernel, nil, []int{N}, nil, nil)
}
queue.Finish()
fmt.Println("GPU kernel avg time:", time.Since(t1)/GPU_REPEAT)
outBytes := make([]byte, len(aBytes))
queue.EnqueueReadBuffer(
bufA,
true,
0,
len(outBytes),
unsafe.Pointer(&outBytes[0]),
nil,
)
out := bytesToF32(outBytes)
fmt.Println("check:", out[0], aCPU[0])
}
9. 実行
go clean -cache
go run .
10. 期待される出力例
CPU avg time: 2.8ms
Using device: NVIDIA GeForce RTX 4060 Laptop GPU
GPU kernel avg time: 0.5ms
GPU copy host->device: 10ms
check: (一致しない値)
11. 結果の正しい理解
-
GPUは 計算だけなら圧倒的に速い
-
転送(CPU↔GPU)が非常に重い
-
転送込みでは CPU の方が速くなるのは正常
-
GPUは
-
データを載せたまま
-
大量・反復・重い計算
で真価を発揮する
-
12. 本検証で得られた結論
-
Go言語からGPU(OpenCL)は 実用的に利用可能
-
GPU性能評価では
-
計算
-
転送
を必ず分けて考える必要がある
-
-
本コードは GPU評価の 最小再現テンプレートとして有用
以上