はしがき
テストもコードベースが巨大になってくると、
時間が掛かるので並列処理したいヨネーって要望とか。
並列処理のベンチマークどないしよか的な事を
まとめてみました。
CIで並列実行してもらえばよくね?
とか言われると、そうですね感溢れるかもですが :P
最近、testの事ばかり調べてたので、メモがてら。
テストの並列実行
testing.T.Parallel()
を呼び出したテストは並列に実行されます。
go test -parallel n(並列数)
で指定すれば並列で実行してくれます。
試してみましょう。
package main
import (
"testing"
"time"
)
// 10秒かかるテスト
func Test10sSleep(t *testing.T) {
t.Parallel()
time.Sleep(10 * time.Second)
}
// 20秒かかるテスト
func Test20sSleep(t *testing.T) {
t.Parallel()
time.Sleep(20 * time.Second)
}
// 30秒かかるテスト
func Test30sSleep(t *testing.T) {
t.Parallel()
time.Sleep(30 * time.Second)
}
- 通常実行
$ go test ./...
ok playground_/paralleltest 60.855s
- 3並列
$ go test ./... -parallel 3
ok playground_/paralleltest 30.025s
並列テストの恩恵を受けてますね :)
注意点
並列処理なので、testAが走っている最中にtestBが走って相互に影響してコケたりします。
折角並列実行しても、成功するときもあれば失敗しちゃうよお ふぇぇぇぇ...
ってなってしまいます。
他のテストに影響を出すような物(例えばDB周りとか)はmock化するか、
並列実行を避けたほうが無難かなと。
テストしたい関数の並列実行時のベンチマークについて
testing.B.RunParallel
が使えます。
SetParallelism(n int)
で 設定値*GOMAXPROCS
を反映する事できます。
p.go
package main
import "time"
// 3秒止める処理
func DoAnyThing() {
time.Sleep(3 * time.Second)
}
p_test.go
package main
import "testing"
func BenchmarkSingle(b *testing.B) {
for i := 0; i < b.N; i++ {
DoAnyThing()
}
}
func BenchmarkParallel(b *testing.B) {
b.SetParallelism(5) // 私の実行環境の場合、GOMAXPROCS=4なので20並列
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
DoAnyThing()
}
})
}
- 結果
$ go test -bench . -benchmem -benchtime 30s -v
testing: warning: no tests to run
PASS
BenchmarkSingle-4 10 3001148727 ns/op 64 B/op 1 allocs/op
BenchmarkParallel-4 200 150105216 ns/op 108 B/op 1 allocs/op
20倍実行できてますね :)
GOMAXPROCは runtime.GOMAXPROCS(0)
で取得できます。