99re热这里只有精品视频,7777色鬼xxxx欧美色妇,国产成人精品一区二三区在线观看,内射爽无广熟女亚洲,精品人妻av一区二区三区

Go 語言 基準測試

2023-03-14 16:59 更新

原文鏈接:https://gopl-zh.github.io/ch11/ch11-04.html


11.4. 基準測試

基準測試是測量一個程序在固定工作負載下的性能。在Go語言中,基準測試函數(shù)和普通測試函數(shù)寫法類似,但是以Benchmark為前綴名,并且?guī)в幸粋€*testing.B類型的參數(shù);*testing.B參數(shù)除了提供和*testing.T類似的方法,還有額外一些和性能測量相關(guān)的方法。它還提供了一個整數(shù)N,用于指定操作執(zhí)行的循環(huán)次數(shù)。

下面是IsPalindrome函數(shù)的基準測試,其中循環(huán)將執(zhí)行N次。

import "testing"

func BenchmarkIsPalindrome(b *testing.B) {
    for i := 0; i < b.N; i++ {
        IsPalindrome("A man, a plan, a canal: Panama")
    }
}

我們用下面的命令運行基準測試。和普通測試不同的是,默認情況下不運行任何基準測試。我們需要通過-bench命令行標志參數(shù)手工指定要運行的基準測試函數(shù)。該參數(shù)是一個正則表達式,用于匹配要執(zhí)行的基準測試函數(shù)的名字,默認值是空的。其中“.”模式將可以匹配所有基準測試函數(shù),但因為這里只有一個基準測試函數(shù),因此和-bench=IsPalindrome參數(shù)是等價的效果。

$ cd $GOPATH/src/gopl.io/ch11/word2
$ go test -bench=.
PASS
BenchmarkIsPalindrome-8 1000000                1035 ns/op
ok      gopl.io/ch11/word2      2.179s

結(jié)果中基準測試名的數(shù)字后綴部分,這里是8,表示運行時對應(yīng)的GOMAXPROCS的值,這對于一些與并發(fā)相關(guān)的基準測試是重要的信息。

報告顯示每次調(diào)用IsPalindrome函數(shù)花費1.035微秒,是執(zhí)行1,000,000次的平均時間。因為基準測試驅(qū)動器開始時并不知道每個基準測試函數(shù)運行所花的時間,它會嘗試在真正運行基準測試前先嘗試用較小的N運行測試來估算基準測試函數(shù)所需要的時間,然后推斷一個較大的時間保證穩(wěn)定的測量結(jié)果。

循環(huán)在基準測試函數(shù)內(nèi)實現(xiàn),而不是放在基準測試框架內(nèi)實現(xiàn),這樣可以讓每個基準測試函數(shù)有機會在循環(huán)啟動前執(zhí)行初始化代碼,這樣并不會顯著影響每次迭代的平均運行時間。如果還是擔心初始化代碼部分對測量時間帶來干擾,那么可以通過testing.B參數(shù)提供的方法來臨時關(guān)閉或重置計時器,不過這些一般很少會用到。

現(xiàn)在我們有了一個基準測試和普通測試,我們可以很容易測試改進程序運行速度的想法。也許最明顯的優(yōu)化是在IsPalindrome函數(shù)中第二個循環(huán)的停止檢查,這樣可以避免每個比較都做兩次:

n := len(letters)/2
for i := 0; i < n; i++ {
    if letters[i] != letters[len(letters)-1-i] {
        return false
    }
}
return true

不過很多情況下,一個顯而易見的優(yōu)化未必能帶來預(yù)期的效果。這個改進在基準測試中只帶來了4%的性能提升。

$ go test -bench=.
PASS
BenchmarkIsPalindrome-8 1000000              992 ns/op
ok      gopl.io/ch11/word2      2.093s

另一個改進想法是在開始為每個字符預(yù)先分配一個足夠大的數(shù)組,這樣就可以避免在append調(diào)用時可能會導(dǎo)致內(nèi)存的多次重新分配。聲明一個letters數(shù)組變量,并指定合適的大小,像下面這樣,

letters := make([]rune, 0, len(s))
for _, r := range s {
    if unicode.IsLetter(r) {
        letters = append(letters, unicode.ToLower(r))
    }
}

這個改進提升性能約35%,報告結(jié)果是基于2,000,000次迭代的平均運行時間統(tǒng)計。

$ go test -bench=.
PASS
BenchmarkIsPalindrome-8 2000000                      697 ns/op
ok      gopl.io/ch11/word2      1.468s

如這個例子所示,快的程序往往是伴隨著較少的內(nèi)存分配。-benchmem命令行標志參數(shù)將在報告中包含內(nèi)存的分配數(shù)據(jù)統(tǒng)計。我們可以比較優(yōu)化前后內(nèi)存的分配情況:

$ go test -bench=. -benchmem
PASS
BenchmarkIsPalindrome    1000000   1026 ns/op    304 B/op  4 allocs/op

這是優(yōu)化之后的結(jié)果:

$ go test -bench=. -benchmem
PASS
BenchmarkIsPalindrome    2000000    807 ns/op    128 B/op  1 allocs/op

用一次內(nèi)存分配代替多次的內(nèi)存分配節(jié)省了75%的分配調(diào)用次數(shù)和減少近一半的內(nèi)存需求。

這個基準測試告訴了我們某個具體操作所需的絕對時間,但我們往往想知道的是兩個不同的操作的時間對比。例如,如果一個函數(shù)需要1ms處理1,000個元素,那么處理10000或1百萬將需要多少時間呢?這樣的比較揭示了漸近增長函數(shù)的運行時間。另一個例子:I/O緩存該設(shè)置為多大呢?基準測試可以幫助我們選擇在性能達標情況下所需的最小內(nèi)存。第三個例子:對于一個確定的工作哪種算法更好?基準測試可以評估兩種不同算法對于相同的輸入在不同的場景和負載下的優(yōu)缺點。

比較型的基準測試就是普通程序代碼。它們通常是單參數(shù)的函數(shù),由幾個不同數(shù)量級的基準測試函數(shù)調(diào)用,就像這樣:

func benchmark(b *testing.B, size int) { /* ... */ }
func Benchmark10(b *testing.B)         { benchmark(b, 10) }
func Benchmark100(b *testing.B)        { benchmark(b, 100) }
func Benchmark1000(b *testing.B)       { benchmark(b, 1000) }

練習(xí) 11.6: 為2.6.2節(jié)的練習(xí)2.4和練習(xí)2.5的PopCount函數(shù)編寫基準測試??纯椿诒砀袼惴ㄔ诓煌闆r下對提升性能會有多大幫助。

練習(xí) 11.7: 為*IntSet(§6.5)的Add、UnionWith和其他方法編寫基準測試,使用大量隨機輸入。你可以讓這些方法跑多快?選擇字的大小對于性能的影響如何?IntSet和基于內(nèi)建map的實現(xiàn)相比有多快?



以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號