ベンチマーク入門



Goのベンチマークは多くの点で単体テストに似ていますが、キーの違いがあり、異なる目的に役立ちます.囲碁での単体テストとして知られていないので、この記事はGoのベンチマークを紹介することを目的としています:どのように作成するには、どのようにそれらを実行するには、どのように結果を読むといくつかの高度なトピックにいくつかのポインタは、外出先でベンチマークテストを作成する.
ベンチマークは、Goコードのパフォーマンスをテストする関数ですtesting 標準ライブラリのパッケージは、このように外部ライブラリに任意の依存関係なしで利用可能です.
ベンチマークを実行するとき、あなたは実行時に関する若干の情報と要求されるならば、ベンチマークの下のコードのメモリフットプリントを提供します.
$go test -benchmem -run ^$ -bench ^(Benchmark1Sort)$
goos: linux
goarch: amd64
Benchmark1Sort-12          10000        105705 ns/op        8224 B/op          2 allocs/op
PASS
ok      _/home/mcaci/code/github.com/mcaci/dev-art/go-bench 1.083s

でベンチマークを作成する


ベンチマークを作成するにはtesting パッケージを作成し、テスト関数が作成されるような方法でベンチマーク関数を作成します.
たとえば、単体テストを定義するとき、func TestAny(t *testing) 初めに、代わりに、ベンチマークを定義するとき、私たちはfunc BenchmarkAny(b *testing.B) .
ユニットテストに関するGoのベンチマークの顕著な違いは、0から0までのループですb.N . ベンチマークの下でコードの性能測定の精度を向上させるのに十分なデータが集められることを確認するために、ベンチマークは複数回実行されます.
野原b.N 固定値ではありませんが、ベンチマーク関数が少なくとも1秒間実行されるように動的に適応されます.
ここでは、ベンチマークとテスト関数の比較を示します.
func Benchmark1Sort(b *testing.B) {
    for i := 0; i < b.N; i++ {
        sort.Ints(generateSlice(1000))
    }
}
func Test1Sort(t *testing.T) {
    slice := generateSlice(1000)
    if len(slice) != 1000 {
        t.Errorf("unexpected slice size: %d", len(slice))
    }
}

ベンチマークの実行


goのベンチマークを実行するための出発点はgo test コマンドとここで我々は我々がユニットテストを実行していないことを確認するために必要なものが表示されます.

基本的な使い方


go test -bench .
単独で.go test ユニットテストのみを実行するので、フラグを追加する必要があります-bench go testを指示してベンチマークも実行します.
具体的には、このコマンドは、現在のパッケージ内のすべての単体テストとベンチマークを実行します.の引数として追加されました-bench フラグ.
"\"値は実際にどのようなベンチマークを実行するかを記述できる正規表現です.例えばgo test -bench ^Benchmark1Sort$ ベンチマーク1 sortという名前のベンチマークを実行します.
ユニットテストを実行するときと同じように、-v VERBOSE用のフラグは、任意の印刷出力と同様に実行されるベンチマークの詳細を表示します.などのパスを追加したり、パスを追加したりします("//..."特定のパッケージ(またはすべてのパッケージとサブパッケージ)のベンチマークを探す.
go test -bench . -v
go test -bench . ./...

ベンチマークのみの実行


すべての単体テストをフィルタアウトするgo test 's実行-run ^$ フラグを使用する.
go test -run ^$ -bench .
-run 単独でどの単位テストを実行すべきかを指定するために使用されます.引数は正規表現です.使用するとき^$ 引数として、現在のパッケージに存在するベンチマークだけを実行するすべてのテストを効果的にフィルタリングします.

複数回走る


単に-count パラメータを指定された数回何度も実行します.すべての実行結果が出力されます.
$ go test -bench ^Benchmark1Sort$ -run ^$ -count 4
goos: linux
goarch: amd64
Benchmark1Sort-12          10207            134834 ns/op
Benchmark1Sort-12           7554            175572 ns/op
Benchmark1Sort-12           7904            148960 ns/op
Benchmark1Sort-12           8568            147594 ns/op
PASS
ok      _/home/mcaci/code/github.com/mcaci/dev-art/go-bench     7.339s
このフラグは、ベンチマークデータの統計解析を行うために、複数の実行結果をサンプリングするときに便利です.

ベンチマーク結果の読み込み


次の例をもう一度取りましょうgo test -bench 出力を調べる.
func Benchmark1Sort(b *testing.B) {
    for i := 0; i < b.N; i++ {
        sort.Ints(generateSlice(1000))
    }
}

実行時に


最初の解析のためにベンチマークを実行しますgo test -bench ^Benchmark1Sort$ -run ^$
$ go test -bench ^Benchmark1Sort$ -run ^$
goos: linux
goarch: amd64
Benchmark1Sort-12           9252            110547 ns/op
PASS
ok      _/home/mcaci/code/github.com/mcaci/dev-art/go-bench     1.053s
出力された出力は、任意のベンチマーク実行に存在します.
  • GOが走る環境についての情報go env GOOS GOARCH (大文字小文字を区別する)
  • この例では、GOOS : LinuxとGoarch : amd 64です.
  • ベンチマーク行は
  • ベンチマーク実行の名前、ベンチマーク1 SORT - 12、それ自体が関数名、ベンチマーク1ソートから構成され、続いてベンチマーク実行に使用されるCPUの数、12.
  • ループが実行された回数9252.
  • テストされた関数の1秒間のナノ秒で表される平均ランタイムsort.Ints(generateSlice(1000)) , この場合は、10547 ns/opとなる.
  • ベンチマークの全体的な状態に関する情報、ベンチマークの下のパッケージ、および実行のための合計時間に関する情報.
  • CPUの数についての簡単な注意-cpu フラッグベンチマークは、フラグで定義されたCPUごとに1回複数回実行されます.
    $ go test -bench ^Benchmark1Sort$ -run ^$ -cpu 1,2,4
    goos: linux
    goarch: amd64
    Benchmark1Sort              9280            113086 ns/op
    Benchmark1Sort-2            9379            117156 ns/op
    Benchmark1Sort-4            8637            118818 ns/op
    PASS
    ok      _/home/mcaci/code/github.com/mcaci/dev-art/go-bench     3.234s
    
    このフラグが省略された場合、デフォルト値はGo変数GOMaxProcsから取られ、CPUの数は1に等しいときに出力されません.

    実行時とメモリ


    出力にメモリフットプリントに関する情報を追加するには、-benchmem フラグは以下の通り.
    $ go test -bench ^Benchmark1Sort$ -run ^$ -benchmem
    
    goos: linux
    goarch: amd64
    Benchmark1Sort-12          10327            116903 ns/op            8224 B/op          2 allocs/op
    PASS
    ok      _/home/mcaci/code/github.com/mcaci/dev-art/go-bench     2.128s
    
    つの新しい列がベンチマーク列の出力に追加されました
  • ベンチマーク、8224 b/opの下での操作によって必要とされるバイト数
  • ベンチマークの下での操作によって行われる割り当ての数
  • より複雑なベンチマークを書く


    ここでは、より複雑なベンチマークを書く方法のいくつかの例があります.

    StartTimer / stoptimer / resettimer


    ベンチマークにコードを実行するために費やされる時間を実際に測定する前にいくつかのセットアップをする必要があるときにStartTimer , StopTimer and ResetTimer 実際にはベンチマークツールによって考慮される必要があるコードのビットを分離するのに役立ちます.
    前のスニペットを取り、ソート操作からスライスの作成を分離し、後者の実行を測定します.
    こうするには、
    func Benchmark2aSort(b *testing.B) {
        for i := 0; i < b.N; i++ {
            b.StopTimer()
            s := generateSlice(1000)
            b.StartTimer()
            sort.Ints(s)
        }
    }
    
    使用によってb.StopTimer() 我々は、実行時にこの時点からベンチマークの一部となることはありませんb.StartTimer() が呼び出されると、各ループでは、sort.Ints(s) ベンチマーク.
    最初にスライスを準備し、ベンチマークのための不変量を作りたいなら、代わりに書くことができます.
    func Benchmark2bSort(b *testing.B) {
        s := generateSlice(1000)
        b.ResetTimer()
        for i := 0; i < b.N; i++ {
            sort.Ints(s)
        }
    }
    
    使用によってb.ResetTimer() 我々はこれまで収集したすべてのデータを破棄し、新たにベンチマークのためのデータの収集を開始し、効果的にgenerateSlice 結果全体を呼び出します.

    ベンチマークテストケースとサブベンチマーク


    テストのように、ベンチマークもサブベンチャマークを作成するテストケースと実行ループの構造から恩恵を受けることができます.
    例を見ましょう.
    func Benchmark3Sort(b *testing.B) {
        benchData := map[string]struct {
            size int
        }{
            "with size 1000":    {size: 1000},
            "with size 10000":   {size: 10000},
            "with size 100000":  {size: 100000},
            "with size 1000000": {size: 1000000},
        }
        b.ResetTimer()
        for benchName, data := range benchData {
            b.StopTimer()
            s := generateSlice(data.size)
            b.StartTimer()
            b.Run(benchName, func(b *testing.B) {
                for i := 0; i < b.N; i++ {
                    sort.Ints(s)
                }
            })
        }
    }
    
    この例ではmap[string]struct{...} テストケースを使った複雑なテストのために、ベンチマークの例とデータを定義し、b.Run(name string, f func(*testing.B)) ベンチマークテストを別々に実行するサブベンチマークを作成します.
    $ go test -bench ^Benchmark3Sort$ -run ^$
    goos: linux
    goarch: amd64
    Benchmark3Sort/with_size_1000000-12                   10         130396565 ns/op
    Benchmark3Sort/with_size_1000-12                   23210             58078 ns/op
    Benchmark3Sort/with_size_10000-12                   1300            865703 ns/op
    Benchmark3Sort/with_size_100000-12                   118           8718656 ns/op
    PASS
    ok      _/home/mcaci/code/github.com/mcaci/dev-art/go-bench     6.670s
    
    ベンチマークの名前がベンチマークの名前を出力するベンチマークの名前/ベンチマークとしてベンチマーク名に追加されます.

    考えを捨てる


    ベンチマークがどのように働いているかを説明し、効果的にそれらを書く方法のより深い知識を得るためにまだ長い道のりがあります.それ自身の記事を必要とする主要なトピックのうちの1つはb.RunParallel しかし、私はこの記事がGoでベンチマークの基礎を与えるのに役立つことを望みます、そして、機能とツールをここで言及していないツールを調査する若干の理由.
    あなたはTwitterで@ iKiForosCount freesまたはここでdev . toを見つけることができます
    そして、私はあなたの質問やコメントを聞くのを楽しみにしています.
    これはミリー、読書のおかげだった!

    参考文献


  • Go's testing package and go cmd testing flags GOチームから
  • デイブチェイニーのものHow to write benchmarks in Go

  • Justforfunc 'によるベンチマークに関する記事
  • 内部のベンチマーク例の源instana.com's Practical Golang benchmarks
  • Dotgo 19会議で与えられたDaniel Marttiによる最新ベンチマークの話Optimizing go code without a blindfold