1 回答

TA貢獻1803條經驗 獲得超3個贊
那不是您應該測試代碼性能的方式。您應該使用 Go 的內置測試框架(testing
包和go test
命令)。
讓我們創(chuàng)建可測試代碼:
func f() {
? ? // Code that must only be run once
}
var testOnce = &sync.Once{}
func DoWithOnce() {
? ? testOnce.Do(f)
}
var (
? ? mu = &sync.Mutex{}
? ? b? bool
)
func DoWithMutex() {
? ? mu.Lock()
? ? if !b {
? ? ? ? f()
? ? ? ? b = true
? ? }
? ? mu.Unlock()
}
讓我們使用該testing包編寫適當的測試/基準測試代碼:
func BenchmarkOnce(b *testing.B) {
? ? for i := 0; i < b.N; i++ {
? ? ? ? DoWithOnce()
? ? }
}
func BenchmarkMutex(b *testing.B) {
? ? for i := 0; i < b.N; i++ {
? ? ? ? DoWithMutex()
? ? }
}
我們可以使用以下代碼運行基準測試:
go test -bench .
以下是基準測試結果:
BenchmarkOnce-4? ? ? ? ?200000000? ? ? ? ? ? ? ? 6.30 ns/op
BenchmarkMutex-4? ? ? ? 100000000? ? ? ? ? ? ? ?20.0 ns/op
PASS
如您所見,使用sync.Once()比使用sync.Mutex. 為什么?因為sync.Once()有一個“優(yōu)化”的短路徑,它只使用原子加載來檢查任務之前是否被調用過,如果是,則不使用互斥鎖。“慢速”路徑可能只在第一次調用Once.Do(). 雖然如果你有許多并發(fā)的 goroutines 試圖調用DoWithOnce(),慢速路徑可能會多次到達,但從長遠來看once.Do()只需要使用原子負載。
并行測試(來自多個 goroutines)
是的,上面的基準測試代碼僅使用單個 goroutine 進行測試。但是使用多個并發(fā) goroutine 只會讓互斥體的情況變得更糟,因為它總是必須獲得一個互斥體來檢查是否要調用任務,而只sync.Once使用原子負載。
盡管如此,讓我們對其進行基準測試。
以下是使用并行測試的基準測試代碼:
func BenchmarkOnceParallel(b *testing.B) {
? ? b.RunParallel(func(pb *testing.PB) {
? ? ? ? for pb.Next() {
? ? ? ? ? ? DoWithOnce()
? ? ? ? }
? ? })
}
func BenchmarkMutexParallel(b *testing.B) {
? ? b.RunParallel(func(pb *testing.PB) {
? ? ? ? for pb.Next() {
? ? ? ? ? ? DoWithMutex()
? ? ? ? }
? ? })
}
我的機器上有 4 個內核,所以我將使用這 4 個內核:
go test -bench Parallel -cpu=4
(您可以省略該-cpu標志,在這種情況下,它默認為GOMAXPROCS– 可用核心數。)
結果如下:
BenchmarkOnceParallel-4? ? ? ? ?500000000? ? ? ? ? ? ? ? 3.04 ns/op
BenchmarkMutexParallel-4? ? ? ? 20000000? ? ? ? ? ? ? ? 93.7 ns/op
當“并發(fā)增加”時,結果開始變得無與倫比sync.Once(在上面的測試中,它快了 30 倍)。
我們可能會進一步增加使用創(chuàng)建的 goroutines 的數量testing.B.SetPralleism(),但是當我將它設置為 100 時我得到了類似的結果(這意味著 400 個 goroutines 被用來調用基準測試代碼)。
- 1 回答
- 0 關注
- 170 瀏覽
添加回答
舉報