🧪
【Go】Goのテストに入門してみた! ~テーブル駆動ベンチマークテスト編~
はじめに
前回は「ベンチマーク」を見ていきました。
今回は「テーブル駆動ベンチマークテスト」に入門します。
前回の記事はこちら!
この記事でわかること
- テーブル駆動テストをベンチマークテストで活用する方法
- テーブル駆動ベンチマークテストのメリットと不要なケース
テーブル駆動ベンチマークテスト
前回の記事で、文字列結合についてのベンチマークをとりました。
今回はそれをテーブル駆動テストで実行してみます。
main_test.go
package main
import (
"fmt"
"strings"
"testing"
)
const N = 100000
func generateStrings() []string {
s := make([]string, N)
for i := 0; i < N; i++ {
s[i] = "Hello"
}
return s
}
func BenchmarkStringConcatTable(b *testing.B) {
s := generateStrings()
benchmarks := []struct {
name string
fn func([]string)
}{
{
name: "Plus",
fn: func(s []string) {
result := ""
for _, v := range s {
result += v + " "
}
_ = result
},
},
{
name: "Sprintf",
fn: func(s []string) {
result := ""
for _, v := range s {
result = fmt.Sprintf("%s %s", result, v)
}
_ = result
},
},
{
name: "Join",
fn: func(s []string) {
_ = strings.Join(s, " ")
},
},
{
name: "Builder",
fn: func(s []string) {
var sb strings.Builder
for _, v := range s {
sb.WriteString(v)
sb.WriteString(" ")
}
_ = sb.String()
},
},
}
for _, bm := range benchmarks {
b.Run(bm.name, func(b *testing.B) {
b.ResetTimer()
for i := 0; i < b.N; i++ {
bm.fn(s)
}
})
}
}
テストを実行すると、以下のような結果を得ることができます。
ターミナル
# go test -benchmem -bench .
goos: darwin
goarch: arm64
pkg: go-test-practice
cpu: Apple M1
BenchmarkStringConcatTable
BenchmarkStringConcatTable/Plus
BenchmarkStringConcatTable/Plus-8 1 5262879083 ns/op 30393265200 B/op 100139 allocs/op
BenchmarkStringConcatTable/Sprintf
BenchmarkStringConcatTable/Sprintf-8 1 5221436792 ns/op 60437814320 B/op 398183 allocs/op
BenchmarkStringConcatTable/Join
BenchmarkStringConcatTable/Join-8 1615 721619 ns/op 606209 B/op 1 allocs/op
BenchmarkStringConcatTable/Builder
BenchmarkStringConcatTable/Builder-8 1312 865771 ns/op 3242753 B/op 31 allocs/op
PASS
ok go-test-practice 14.320s
テーブル駆動テストでベンチマークをとるメリット
- ベンチマーク対象を増やしても、テーブルにエントリを追加するだけで済む
- 共通の準備(generateStrings)を1回だけで済ませて効率的
- コードの重複を避け、保守性が上がる
ただし、単純な関数の実行時間だけを測りたいときや、テスト条件が増えない場合などは、テーブル駆動テスト形式でベンチマークをとる必要はなさそうです。
まとめ
今回は「テーブル駆動ベンチマークテスト」について学びました。
テーブル形式にすることで、関数ごとのベンチマーク結果を見やすく整理でき、関数の追加も簡単です。
また、共通処理をまとめることで、コードの再利用性や保守性が向上します。
ただし、テスト対象が1つしかないなど、比較が不要な場合には通常のベンチマークのほうがシンプルで適しています。
次回は「Fuzzingテスト」に入門します!
参考
Discussion