goやdepは適当に入れておくとする
blas
macの場合は以下。OpenBLAS以外でも、MKL、Atlasでも良さそう。試してはいない。
brew install openblas
これで、/usr/local/opt/openblas/
以下にヘッダとライブラリが入る
ビルドするとき
gonumの取得は普通にdep ensure
とかすれば良い。depなどを使わない場合は、gonumを入れた上で、ドキュメントの通り、gonum/netlibパッケージをCGO_LDFLAGを指定してgo install
する。
CGO_LDFLAGS="-L/usr/local/opt/openblas/lib -lopenblas" go install gonum.org/v1/netlib/blas/netlib
CGO_LDFLAGS="-L/usr/local/opt/openblas/lib -lopenblas" go install gonum.org/v1/netlib/lapack/netlib
gonumの行列・ベクトル計算をOpenBLASを利用するようにする場合は、以下のような設定が必要
import(
"gonum.org/v1/gonum/blas/blas64"
"gonum.org/v1/netlib/blas/netlib"
)
func init(){
blas64.Use(netlib.Implementation{})
}
ここではinitに書いているが、プログラムの初期化時に一度呼び出されるように書いておけばどこでも良さそう。
go install
した場合はいらないと思うが、depに入れた場合は、go build
やgo test
をする場合にはCGO_LDFLAGS
にblasのパスを指定して実行しなければリンクエラーになる。direnvなどに書いておこう。
適当なベンチマークコード
10万*100の行列と100次元のベクトルの行列ベクトル積をスッと計算してみる。基本行列を作るほうが重いので、init
でfillしている。
package main
import (
"math/rand"
"testing"
"gonum.org/v1/gonum/blas/blas64"
"gonum.org/v1/gonum/mat"
"gonum.org/v1/netlib/blas/netlib"
)
var (
m *mat.Dense
v *mat.VecDense
)
func init() {
blas64.Use(netlib.Implementation{})
m = makeRandMat(100000, 100)
v = makeRandVec(100)
}
func makeRandMat(row, col int) *mat.Dense {
data := make([]float64, row*col)
for i := range data {
data[i] = rand.NormFloat64()
}
return mat.NewDense(row, col, data)
}
func makeRandVec(num int) *mat.VecDense {
data := make([]float64, num)
for i := range data {
data[i] = rand.NormFloat64()
}
return mat.NewVecDense(num, data)
}
func BenchmarkMatVecDot(b *testing.B) {
res := mat.NewVecDense(100000, nil)
for i := 0; i < b.N; i++ {
res.MulVec(m, v)
}
}
blas64.Use(netlib.Implementation{})
をコメントアウトした場合としなかった場合でだいたい50%弱高速化している。まぁbrewで適当に入れたOpenBLASとの比較ならこんなものな気はする。というかgonumのデフォルトgo実装もそれなりに早いのね。
MacBook Pro (13-inch, 2017, Four Thunderbolt 3 Ports)
プロセッサ: 3.5GHz Intel Corei7
goos: darwin
goarch: amd64
pkg: hoge
BenchmarkMatVecDot-4 300 4018419 ns/op
PASS
goos: darwin
goarch: amd64
pkg: hoge
BenchmarkMatVecDot-4 200 7239200 ns/op
PASS
alpineで使う
Dockerfileはこんな感じ
FROM golang:1.12.9-alpine3.10 as builder
ENV CGO_ENABLED=0
ENV GOOS=linux
ENV GOARCH=amd64
RUN apk add --no-cache ca-certificates git build-base openblas-dev
WORKDIR /go/src/{PACKAGE_PATH}
ENV GO111MODULE=on
ENV CGO_ENABLED=1
ENV CGO_LDFLAGS="-L/usr/lib -lopenblas"
COPY go.mod .
COPY go.sum .
RUN go mod download
COPY . .
RUN mkdir -p bin
RUN go build -o bin/{EXECUTABLE} ./{PATH_TO_COMMAND}/main.go
# runtime image
FROM alpine
RUN apk add --no-cache ca-certificates openblas
WORKDIR /var/app
COPY --from=builder /go/src/{PACKAGE_PATH}/bin/{EXECUTABLE} /var/app/{EXECUTABLE}
ENTRYPOINT ["./{EXECUTABLE}"]