Go語言精進之路讀書筆記第46條——為被測物件建立效能基準

brynchen發表於2024-03-10

46.1 效能基準測試在Go語言中是“一等公民”

效能基準測試在Go語言中和普通的單元測試一樣被原生支援的,得到的是“一等公民”的待遇。

我們可以像對普通單元測試那樣在*_test.go檔案中建立被測物件的效能基準測試,每個以Benchmark字首開頭的函式都會被當作一個獨立的效能基準測試。

46.2 順序執行和並行執行的效能基準測試

#透過-benchtime手動指定b.N的值
go test -v -benchtime 5x -bench . sequential_test.go

#透過-count手動指定執行次數
go test -v -count 2 -bench . sequential_test.go

#透過-cpu手動指定GOMAXPROCS
go test -v -bench . paralell_test.go -cpu 2,4,8
go test -v -bench . paralell_test.go -cpu=2

1.順序執行

var (
    m     map[int64]struct{} = make(map[int64]struct{}, 10)
    mu    sync.Mutex
    round int64 = 1
)

func BenchmarkSequential(b *testing.B) {
    fmt.Printf("\ngoroutine[%d] enter BenchmarkSequential: round[%d], b.N[%d]\n",
        tls.ID(), atomic.LoadInt64(&round), b.N)
    defer func() {
        atomic.AddInt64(&round, 1)
    }()

    for i := 0; i < b.N; i++ {
        mu.Lock()
        _, ok := m[round]
        if !ok {
            m[round] = struct{}{}
            fmt.Printf("goroutine[%d] enter loop in BenchmarkSequential: round[%d], b.N[%d]\n",
                tls.ID(), atomic.LoadInt64(&round), b.N)
        }
        mu.Unlock()
    }
    fmt.Printf("goroutine[%d] exit BenchmarkSequential: round[%d], b.N[%d]\n",
        tls.ID(), atomic.LoadInt64(&round), b.N)
}

2.並行執行

var (
    m     map[int64]int = make(map[int64]int, 20)
    mu    sync.Mutex
    round int64 = 1
)

func BenchmarkParalell(b *testing.B) {
    fmt.Printf("\ngoroutine[%d] enter BenchmarkParalell: round[%d], b.N[%d]\n",
        tls.ID(), atomic.LoadInt64(&round), b.N)
    defer func() {
        atomic.AddInt64(&round, 1)
    }()

    b.RunParallel(func(pb *testing.PB) {
        id := tls.ID()
        fmt.Printf("goroutine[%d] enter loop func in BenchmarkParalell: round[%d], b.N[%d]\n", tls.ID(), atomic.LoadInt64(&round), b.N)
        for pb.Next() {
            mu.Lock()
            _, ok := m[id]
            if !ok {
                m[id] = 1
            } else {
                m[id] = m[id] + 1
            }
            mu.Unlock()
        }

        mu.Lock()
        count := m[id]
        mu.Unlock()

        fmt.Printf("goroutine[%d] exit loop func in BenchmarkParalell: round[%d], loop[%d]\n", tls.ID(), atomic.LoadInt64(&round), count)
    })

    fmt.Printf("goroutine[%d] exit BenchmarkParalell: round[%d], b.N[%d]\n",
        tls.ID(), atomic.LoadInt64(&round), b.N)
}

46.3 使用效能基準比較工具

benchcmp已廢棄,官方推薦用經過統計學方法處理的benchstat。

46.4 排除額外干擾,讓基準測試更精確

使用testing.B提供的定時器操作方法排除額外干擾,讓基準測試更精確,但不要在RunParallel中使用ResetTimer、StartTimer、StopTimer,因為它們具有全域性副作用。

相關文章