官术网_书友最值得收藏!

46.3 使用性能基準(zhǔn)比較工具

現(xiàn)在我們已經(jīng)可以通過(guò)Go原生提供的性能基準(zhǔn)測(cè)試為被測(cè)對(duì)象建立性能基準(zhǔn)了。但被測(cè)代碼更新前后的性能基準(zhǔn)比較依然要靠人工計(jì)算和肉眼比對(duì),十分不方便。為此,Go核心團(tuán)隊(duì)先后開(kāi)發(fā)了兩款性能基準(zhǔn)比較工具:benchcmp(https://github.com/golang/tools/tree/master/cmd/benchcmp)和benchstat(https://github.com/golang/perf/tree/master/benchstat)。

1. benchcmp

benchcmp上手快,簡(jiǎn)單易用,對(duì)于輸出的比較結(jié)果我們無(wú)須參考文檔幫助即可自行解讀。下面看一個(gè)使用benchcmp進(jìn)行性能基準(zhǔn)比較的例子。

// chapter8/sources/benchmark-compare/strcat_test.go

var sl = []string{
    "Rob Pike ",
    "Robert Griesemer ",
    "Ken Thompson ",
}

func Strcat(sl []string) string {
    return concatStringByOperator(sl)
}

func concatStringByOperator(sl []string) string {
    var s string
    for _, v := range sl {
        s += v
    }
    return s
}

func concatStringByJoin(sl []string) string {
    return strings.Join(sl, "")
}

func BenchmarkStrcat(b *testing.B) {
    for n := 0; n < b.N; n++ {
        Strcat(sl)
    }
}

上面例子中的被測(cè)目標(biāo)為Strcat。最初Strcat使用通過(guò)Go原生的操作符("+")連接的方式實(shí)現(xiàn)了字符串的連接。我們采集一下它的性能基準(zhǔn)數(shù)據(jù):

$go test -run=NONE -bench . strcat_test.go > old.txt

然后,升級(jí)Strcat的實(shí)現(xiàn),采用strings.Join函數(shù)來(lái)實(shí)現(xiàn)多個(gè)字符串的連接:

func Strcat(sl []string) string {
    return concatStringByJoin(sl)
}

再采集優(yōu)化后的性能基準(zhǔn)數(shù)據(jù):

$go test -run=NONE -bench . strcat_test.go > new.txt

接下來(lái)就輪到benchcmp登場(chǎng)了:

$benchcmp old.txt new.txt
benchmark             old ns/op     new ns/op     delta
BenchmarkStrcat-8     92.4          49.6          -46.32%

我們看到,benchcmp接受被測(cè)代碼更新前后的兩次性能基準(zhǔn)測(cè)試結(jié)果文件——old.txt和new.txt,并將這兩個(gè)文件中的相同基準(zhǔn)測(cè)試(比如這里的BenchmarkStrcat)的輸出結(jié)果進(jìn)行比較。

如果使用-count對(duì)BenchmarkStrcat執(zhí)行多次,那么benchcmp給出的結(jié)果如下:

$go test -run=NONE -count 5 -bench . strcat_test.go > old.txt
$go test -run=NONE -count 5 -bench . strcat_test.go > new.txt

$benchcmp old.txt new.txt
benchmark             old ns/op     new ns/op     delta
BenchmarkStrcat-8     92.8          51.4          -44.61%
BenchmarkStrcat-8     91.9          55.3          -39.83%
BenchmarkStrcat-8     96.1          52.6          -45.27%
BenchmarkStrcat-8     89.4          50.2          -43.85%
BenchmarkStrcat-8     91.2          51.5          -43.53%

如果向benchcmp傳入-best命令行選項(xiàng),benchcmp將分別從old.txt和new.txt中挑選性能最好的一條數(shù)據(jù),然后進(jìn)行比較:

$benchcmp -best old.txt new.txt
benchmark             old ns/op     new ns/op     delta
BenchmarkStrcat-8     89.4          50.2          -43.85%

benchcmp還可以按性能基準(zhǔn)數(shù)據(jù)前后變化的大小對(duì)輸出結(jié)果進(jìn)行排序(通過(guò)-mag命令行選項(xiàng)):

$benchcmp -mag old.txt new.txt
benchmark             old ns/op     new ns/op     delta
BenchmarkStrcat-8     96.1          52.6          -45.27%
BenchmarkStrcat-8     92.8          51.4          -44.61%
BenchmarkStrcat-8     89.4          50.2          -43.85%
BenchmarkStrcat-8     91.2          51.5          -43.53%
BenchmarkStrcat-8     91.9          55.3          -39.83%

不過(guò)性能基準(zhǔn)測(cè)試的輸出結(jié)果受到很多因素的影響,比如:同一測(cè)試的運(yùn)行次數(shù);性能基準(zhǔn)測(cè)試與其他正在運(yùn)行的程序共享一臺(tái)機(jī)器;運(yùn)行測(cè)試的系統(tǒng)本身就在虛擬機(jī)上,與其他虛擬機(jī)共享硬件;現(xiàn)代機(jī)器的一些節(jié)能和功率縮放(比如CPU的自動(dòng)降頻和睿頻)等。這些因素都會(huì)造成即便是對(duì)同一個(gè)基準(zhǔn)測(cè)試進(jìn)行多次運(yùn)行,輸出的結(jié)果也可能有較大偏差。但benchcmp工具并不關(guān)心這些結(jié)果數(shù)據(jù)在統(tǒng)計(jì)學(xué)層面是否有效,只對(duì)結(jié)果做簡(jiǎn)單比較。

2. benchstat

為了提高對(duì)性能基準(zhǔn)數(shù)據(jù)比較的科學(xué)性,Go核心團(tuán)隊(duì)又開(kāi)發(fā)了benchstat這款工具以替代benchcmp。下面用benchstat比較一下上面例子中的性能基準(zhǔn)數(shù)據(jù):

$benchstat old.txt new.txt
name      old time/op    new time/op   delta
Strcat-8  92.3ns ± 4%   52.2ns ± 6%   -43.43%  (p=0.008 n=5+5)

我們看到,即便old.txt和new.txt中各自有5次運(yùn)行的數(shù)據(jù),benchstat也不會(huì)像benchcmp那樣輸出5行比較結(jié)果,而是輸出一行經(jīng)過(guò)統(tǒng)計(jì)學(xué)方法處理后的比較結(jié)果。以第二列數(shù)據(jù)92.3ns ± 4%為例,這是benchcmp對(duì)old.txt中的數(shù)據(jù)進(jìn)行處理后的結(jié)果,其中±4%是樣本數(shù)據(jù)中最大值和最小值距樣本平均值的最大偏差百分比。如果這個(gè)偏差百分比大于5%,則說(shuō)明樣本數(shù)據(jù)質(zhì)量不佳,有些樣本數(shù)據(jù)是不可信的。由此可以看出,這里new.txt中的樣本數(shù)據(jù)是質(zhì)量不佳的。

benchstat輸出結(jié)果的最后一列(delta)為兩次基準(zhǔn)測(cè)試對(duì)比的變化量。我們看到,采用strings.Join方法連接字符串的平均耗時(shí)比采用原生操作符連接字符串短43%,這個(gè)指標(biāo)后面括號(hào)中的p=0.008是一個(gè)用于衡量?jī)蓚€(gè)樣本集合的均值是否有顯著差異的指標(biāo)。benchstat支持兩種檢驗(yàn)算法:一種是UTest(Mann Whitney UTest,曼-惠特尼U檢驗(yàn)),UTest是默認(rèn)的檢驗(yàn)算法;另外一種是Welch T檢驗(yàn)(TTest)。一般p值小于0.05的結(jié)果是可接受的。

上述兩款工具都支持對(duì)內(nèi)存分配數(shù)據(jù)情況的前后比較,這里以benchstat為例:

$go test -run=NONE -count 5 -bench . strcat_test.go -benchmem > old_with_mem.txt
$go test -run=NONE -count 5 -bench . strcat_test.go -benchmem > new_with_mem.txt

$benchstat old_with_mem.txt new_with_mem.txt
name      old time/op    new time/op    delta
Strcat-8    90.5ns ± 1%    50.6ns ± 2%  -44.14%  (p=0.008 n=5+5)

name      old alloc/op   new alloc/op   delta
Strcat-8     80.0B ± 0%     48.0B ± 0%  -40.00%  (p=0.008 n=5+5)

name      old allocs/op  new allocs/op  delta
Strcat-8      2.00 ± 0%      1.00 ± 0%  -50.00%  (p=0.008 n=5+5)

關(guān)于內(nèi)存分配情況對(duì)比的輸出獨(dú)立于執(zhí)行時(shí)間的輸出,但結(jié)構(gòu)上是一致的(輸出列含義相同),這里就不再贅述了。

Go核心團(tuán)隊(duì)已經(jīng)給benchcmp工具打上了“deprecation”(不建議使用)的標(biāo)簽,因此建議大家使用benchstat來(lái)進(jìn)行性能基準(zhǔn)數(shù)據(jù)的比較。

主站蜘蛛池模板: 获嘉县| 彰武县| 甘肃省| 景德镇市| 临安市| 汽车| 大姚县| 咸宁市| 赫章县| 定南县| 四会市| 连山| 义乌市| 始兴县| 澄城县| 科技| 隆林| 额敏县| 台东县| 浦城县| 资兴市| 泗水县| 隆昌县| 通江县| 射洪县| 宣威市| 内黄县| 富阳市| 汉源县| 民勤县| 麻栗坡县| 北海市| 宜昌市| 江永县| 通山县| 江达县| 和林格尔县| 贵阳市| 松江区| 乐至县| 彭山县|