回到简体

This commit is contained in:
chai2010
2016-02-15 11:06:34 +08:00
parent 9e878f9944
commit 2b37b23285
177 changed files with 2354 additions and 2354 deletions

View File

@@ -1,8 +1,8 @@
## 11.4. 基準測試
## 11.4. 基准测试
準測試是測量一程序在固定工作負載下的性能。在Go言中,基準測試函數和普通測試函數寫法類但是以Benchmark爲前綴名,併且帶有一`*testing.B`型的參數`*testing.B`參數除了提供和`*testing.T`似的方法,還有額外一些和性能量相的方法。它提供了一個整數N指定操作行的循環次數
准测试是测量一程序在固定工作负载下的性能。在Go言中,基准测试函数和普通测试函数写法类但是以Benchmark为前缀名,并且带有一`*testing.B`型的参数`*testing.B`参数除了提供和`*testing.T`似的方法,还有额外一些和性能量相的方法。它提供了一个整数N指定操作行的循环次数
下面是IsPalindrome函的基準測試,其中循環將執行N次。
下面是IsPalindrome函的基准测试,其中循环将执行N次。
```Go
import "testing"
@@ -14,7 +14,7 @@ func BenchmarkIsPalindrome(b *testing.B) {
}
```
用下面的命令行基準測試。和普通測試不同的是,默認情況下不行任何基準測試。我需要通`-bench`命令行標誌參數手工指定要行的基準測試函數。該參數是一個正則表達式,用匹配要行的基準測試函數的名字,默值是空的。其中“.”模式可以匹配所有基準測試函數,但是這里總共隻有一個基準測試函數,因此和`-bench=IsPalindrome`參數是等的效果。
用下面的命令行基准测试。和普通测试不同的是,默认情况下不行任何基准测试。我需要通`-bench`命令行标志参数手工指定要行的基准测试函数。该参数是一个正则表达式,用匹配要行的基准测试函数的名字,默值是空的。其中“.”模式可以匹配所有基准测试函数,但是这里总共只有一个基准测试函数,因此和`-bench=IsPalindrome`参数是等的效果。
```
$ cd $GOPATH/src/gopl.io/ch11/word2
@@ -24,13 +24,13 @@ BenchmarkIsPalindrome-8 1000000 1035 ns/op
ok gopl.io/ch11/word2 2.179s
```
果中基準測試名的數字後綴部分,里是8表示運行時對應的GOMAXPROCS的值這對於一些和併發相關的基準測試是重要的信息。
果中基准测试名的数字后缀部分,里是8表示运行时对应的GOMAXPROCS的值这对于一些和并发相关的基准测试是重要的信息。
報告顯示每次調用IsPalindrome函數花費1.035微秒,是行1,000,000次的平均時間。因爲基準測試驅動器開始時併不知道每個基準測試函數運行所花的時間它會嚐試在眞正運行基準測試前先嚐試用較小的N運行測試來估算基準測試函數所需要的時間然後推斷一個較大的時間保證穩定的測量結果。
报告显示每次用IsPalindrome函数花费1.035微秒,是行1,000,000次的平均时间。因为基准测试驱动器开始时并不知道每个基准测试函数运行所花的时间它会尝试在真正运行基准测试前先尝试用较小的N运行测试来估算基准测试函数所需要的时间然后推断一个较大的时间保证稳定的测量结果。
在基準測試函數內實現,而不是放在基準測試框架內實現,這樣可以讓每個基準測試函數有機會在循環啟動前執行初始化代碼,這樣併不會顯著影每次迭代的平均運行時間。如果還是擔心初始化代部分對測量時間帶來榦擾,那可以通testing.B參數提供的方法來臨時關閉或重置計時器,不過這些一般很少用到。
在基准测试函数内实现,而不是放在基准测试框架内实现,这样可以让每个基准测试函数有机会在循环启动前执行初始化代码,这样并不会显著影每次迭代的平均运行时间。如果还是担心初始化代部分对测量时间带来干扰,那可以通testing.B参数提供的方法来临时关闭或重置计时器,不过这些一般很少用到。
在我有了一個基準測試和普通測試,我可以很容易測試新的程序行更快的想法。也最明顯的優化是在IsPalindrome函中第二個循環的停止檢査,這樣可以避免每個比較都做次:
在我有了一个基准测试和普通测试,我可以很容易测试新的程序行更快的想法。也最明显的优化是在IsPalindrome函中第二个循环的停止检查,这样可以避免每个比较都做次:
```Go
n := len(letters)/2
@@ -42,7 +42,7 @@ for i := 0; i < n; i++ {
return true
```
很多情下,一個明顯的優化併不一定就能代碼預期的效果。這個改進在基準測試中隻帶來了4%的性能提
很多情下,一个明显的优化并不一定就能代码预期的效果。这个改进在基准测试中只带来了4%的性能提
```
$ go test -bench=.
@@ -51,7 +51,7 @@ BenchmarkIsPalindrome-8 1000000 992 ns/op
ok gopl.io/ch11/word2 2.093s
```
另一個改進想法是在開始爲每個字符先分配一個足夠大的數組,這樣就可以避免在append調用時可能會導致內存的多次重新分配。明一letters數組變量,指定合的大小,像下面這樣
另一个改进想法是在开始为每个字符先分配一个足够大的数组,这样就可以避免在append调用时可能会导致内存的多次重新分配。明一letters数组变量,指定合的大小,像下面这样
```Go
letters := make([]rune, 0, len(s))
@@ -62,7 +62,7 @@ for _, r := range s {
}
```
這個改進提陞性能35%報告結果是基2,000,000次迭代的平均運行時間統計
这个改进提升性能35%报告结果是基2,000,000次迭代的平均运行时间统计
```
$ go test -bench=.
@@ -71,7 +71,7 @@ BenchmarkIsPalindrome-8 2000000 697 ns/op
ok gopl.io/ch11/word2 1.468s
```
這個例子所示,快的程序往往是伴隨着較少的存分配。`-benchmem`命令行標誌參數將在報告中包含存的分配數據統計。我可以比較優化前後內存的分配情
这个例子所示,快的程序往往是伴随着较少的存分配。`-benchmem`命令行标志参数将在报告中包含存的分配数据统计。我可以比较优化前后内存的分配情
```
$ go test -bench=. -benchmem
@@ -79,7 +79,7 @@ PASS
BenchmarkIsPalindrome 1000000 1026 ns/op 304 B/op 4 allocs/op
```
這是優化之後的結果:
这是优化之后的结果:
```
$ go test -bench=. -benchmem
@@ -87,11 +87,11 @@ PASS
BenchmarkIsPalindrome 2000000 807 ns/op 128 B/op 1 allocs/op
```
用一次存分配代替多次的存分配省了75%的分配調用次數和減少近一半的存需求。
用一次存分配代替多次的存分配省了75%的分配用次数和减少近一半的存需求。
這個基準測試告訴我們所需的絶對時間依賴給定的具操作,兩個不同的操作所需時間的差也是和不同境相的。例如,如果一個函數需要1ms理1,000元素,那麽處理10000或1百萬將需要多少時間呢?這樣的比揭示了近增長函數的運行時間。另一例子I/O緩存該設置爲多大呢?基準測試可以助我們選擇較小的存但能帶來滿意的性能。第三例子:對於一個確定的工作那算法更好?基準測試可以評估兩種不同算法對於相同的入在不同的景和負載下的優缺點
这个基准测试告诉我们所需的绝对时间依赖给定的具操作,两个不同的操作所需时间的差也是和不同境相的。例如,如果一个函数需要1ms理1,000元素,那么处理10000或1百万将需要多少时间呢?这样的比揭示了近增长函数的运行时间。另一例子I/O缓存该设置为多大呢?基准测试可以助我们选择较小的存但能带来满意的性能。第三例子:对于一个确定的工作那算法更好?基准测试可以评估两种不同算法对于相同的入在不同的景和负载下的优缺点
一般比較基準測試都是結構類似的代。它通常是采用一個參數的函數,從幾個標誌的基準測試函數入口調用,就像這樣
一般比较基准测试都是结构类似的代。它通常是采用一个参数的函数,从几个标志的基准测试函数入口用,就像这样
```Go
func benchmark(b *testing.B, size int) { /* ... */ }
@@ -100,13 +100,13 @@ func Benchmark100(b *testing.B) { benchmark(b, 100) }
func Benchmark1000(b *testing.B) { benchmark(b, 1000) }
```
過函數參數來指定入的大小,但是參數變量對於每個具體的基準測試都是固定的。要避免直接改b.N來控製輸入的大小。除非你它作爲一個固定大小的迭代計算輸入,否則基準測試的結果將毫無意義
过函数参数来指定入的大小,但是参数变量对于每个具体的基准测试都是固定的。要避免直接改b.N来控制输入的大小。除非你它作为一个固定大小的迭代计算输入,否则基准测试的结果将毫无意义
準測試對於編寫代碼是很有助的,但是使工作完成了也應當保存基準測試代碼。因爲隨着項目的展,或者是入的增加,或者是部署到新的操作繫統或不同的理器,我可以再次用基準測試來幫助我們改進設計
准测试对于编写代码是很有助的,但是使工作完成了也应当保存基准测试代码。因为随着项目的展,或者是入的增加,或者是部署到新的操作系统或不同的理器,我可以再次用基准测试来帮助我们改进设计
**練習 11.6:** 2.6.2節的練習2.4和練習2.5的PopCount函數編寫基準測試。看看基表格算法在不同情況下對提陞性能有多大助。
**练习 11.6:** 2.6.2节的练习2.4和练习2.5的PopCount函数编写基准测试。看看基表格算法在不同情况下对提升性能有多大助。
**練習 11.7:** \*IntSet§6.5的Add、UnionWith和其他方法編寫基準測試,使用大量隨機輸入。你可以讓這些方法跑多快?選擇字的大小對於性能的影如何IntSet和基於內建map的實現相比有多快?
**练习 11.7:** \*IntSet§6.5的Add、UnionWith和其他方法编写基准测试,使用大量随机输入。你可以让这些方法跑多快?选择字的大小对于性能的影如何IntSet和基于内建map的实现相比有多快?