您好,登錄后才能下訂單哦!
基準(zhǔn)測(cè)試,是一種測(cè)試代碼性能的方法,比如你有多種不同的方案,都可以解決問(wèn)題,那么到底是那種方案性能更好呢?這時(shí)候基準(zhǔn)測(cè)試就派上用場(chǎng)了。
基準(zhǔn)測(cè)試主要是通過(guò)測(cè)試CPU和內(nèi)存的效率問(wèn)題,來(lái)評(píng)估被測(cè)試代碼的性能,進(jìn)而找到更好的解決方案。比如鏈接池的數(shù)量不是越多越好,那么哪個(gè)值才是最優(yōu)值呢,這就需要配合基準(zhǔn)測(cè)試不斷調(diào)優(yōu)了。
基準(zhǔn)測(cè)試代碼的編寫和單元測(cè)試非常相似,它也有一定的規(guī)則,我們先看一個(gè)示例。
itoa_test.go
func BenchmarkSprintf(b *testing.B){
num:=10
b.ResetTimer()
for i:=0;i<b.N;i++{
fmt.Sprintf("%d",num)
}
}
這是一個(gè)基準(zhǔn)測(cè)試的例子,從中我們可以看出以下規(guī)則:
基準(zhǔn)測(cè)試的代碼文件必須以_test.go結(jié)尾。
基準(zhǔn)測(cè)試的函數(shù)必須以Benchmark開(kāi)頭,必須是可導(dǎo)出的。
基準(zhǔn)測(cè)試函數(shù)必須接受一個(gè)指向Benchmark類型的指針作為唯一參數(shù)。
基準(zhǔn)測(cè)試函數(shù)不能有返回值。
b.ResetTimer是重置計(jì)時(shí)器,這樣可以避免for循環(huán)之前的初始化代碼的干擾。
最后的for循環(huán)很重要,被測(cè)試的代碼要放到循環(huán)里。
b.N是基準(zhǔn)測(cè)試框架提供的,表示循環(huán)的次數(shù),因?yàn)樾枰磸?fù)調(diào)用測(cè)試的代碼,才可以評(píng)估性能。
下面我們運(yùn)行下基準(zhǔn)測(cè)試,看看效果。
? hello go test -bench=. -run=none
BenchmarkSprintf-8 20000000 117 ns/op
PASS
ok flysnow.org/hello 2.474s
運(yùn)行基準(zhǔn)測(cè)試也要使用go test命令,不過(guò)我們要加上-bench=標(biāo)記,它接受一個(gè)表達(dá)式作為參數(shù),匹配基準(zhǔn)測(cè)試的函數(shù),.表示運(yùn)行所有基準(zhǔn)測(cè)試。
因?yàn)槟J(rèn)情況下go test會(huì)運(yùn)行單元測(cè)試,為了防止單元測(cè)試的輸出影響我們查看基準(zhǔn)測(cè)試的結(jié)果,可以使用-run=匹配一個(gè)從來(lái)沒(méi)有的單元測(cè)試方法,過(guò)濾掉單元測(cè)試的輸出,我們這里使用none,因?yàn)槲覀兓旧喜粫?huì)創(chuàng)建這個(gè)名字的單元測(cè)試方法。
下面著重解釋下說(shuō)出的結(jié)果,看到函數(shù)后面的-8了嗎?這個(gè)表示運(yùn)行時(shí)對(duì)應(yīng)的GOMAXPROCS的值。接著的20000000表示運(yùn)行for循環(huán)的次數(shù),也就是調(diào)用被測(cè)試代碼的次數(shù),最后的117 ns/op表示每次需要話費(fèi) 117 納秒。
以上是測(cè)試時(shí)間默認(rèn)是 1 秒,也就是 1 秒的時(shí)間,調(diào)用兩千萬(wàn)次,每次調(diào)用花費(fèi) 117 納秒。如果想讓測(cè)試運(yùn)行的時(shí)間更長(zhǎng),可以通過(guò)-benchtime指定,比如 3 秒。
? hello go test -bench=. -benchtime=3s -run=none
BenchmarkSprintf-8 50000000 109 ns/op
PASS
ok flysnow.org/hello 5.628s
可以發(fā)現(xiàn),我們加長(zhǎng)了測(cè)試時(shí)間,測(cè)試的次數(shù)變多了,但是最終的性能結(jié)果:每次執(zhí)行的時(shí)間,并沒(méi)有太大變化。一般來(lái)說(shuō)這個(gè)值最好不要超過(guò)3秒,意義不大。
上面那個(gè)基準(zhǔn)測(cè)試的例子,其實(shí)是一個(gè)int類型轉(zhuǎn)為string類型的例子,標(biāo)準(zhǔn)庫(kù)里還有幾種方法,我們看下哪種性能更加。
func BenchmarkSprintf(b *testing.B){
num:=10
b.ResetTimer()
for i:=0;i<b.N;i++{
fmt.Sprintf("%d",num)
}}func BenchmarkFormat(b *testing.B){
num:=int64(10)
b.ResetTimer()
for i:=0;i<b.N;i++{
strconv.FormatInt(num,10)
}}func BenchmarkItoa(b *testing.B){
num:=10
b.ResetTimer()
for i:=0;i<b.N;i++{
strconv.Itoa(num)
}
}
運(yùn)行基準(zhǔn)測(cè)試,看看結(jié)果:
? hello go test -bench=. -run=none BenchmarkSprintf-8 20000000 117 ns/op
BenchmarkFormat-8 50000000 33.3 ns/op
BenchmarkItoa-8 50000000 34.9 ns/op
PASS
ok flysnow.org/hello 5.951s
從結(jié)果上看strconv.FormatInt函數(shù)是最快的,其次是strconv.Itoa,然后是fmt.Sprintf最慢,前兩個(gè)函數(shù)性能達(dá)到了最后一個(gè)的 3 倍多。那么最后一個(gè)為什么這么慢的,我們?cè)偻ㄟ^(guò)-benchmem找到根本原因。
? hello go test -bench=. -benchmem -run=none
BenchmarkSprintf-8 20000000 110 ns/op 16 B/op 2 allocs/op
BenchmarkFormat-8 50000000 31.0 ns/op 2 B/op 1 allocs/op
BenchmarkItoa-8 50000000 33.1 ns/op 2 B/op 1 allocs/op
PASS
ok flysnow.org/hello 5.610s
-benchmem可以提供每次操作分配內(nèi)存的次數(shù),以及每次操作分配的字節(jié)數(shù)。從結(jié)果我們可以看到,性能高的兩個(gè)函數(shù),每次操作都是進(jìn)行 1 次內(nèi)存分配,而最慢的那個(gè)要分配 2 次;性能高的每次操作分配 2 個(gè)字節(jié)內(nèi)存,而慢的那個(gè)函數(shù)每次需要分配 16 字節(jié)的內(nèi)存。從這個(gè)數(shù)據(jù)我們就知道它為什么這么慢了,內(nèi)存分配都占用都太高。
在代碼開(kāi)發(fā)中,對(duì)于我們要求性能的地方,編寫基準(zhǔn)測(cè)試非常重要,這有助于我們開(kāi)發(fā)出性能更好的代碼。不過(guò)性能、可用性、復(fù)用性等也要有一個(gè)相對(duì)的取舍,不能為了追求性能而過(guò)度優(yōu)化。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。