make loop

This commit is contained in:
chai2010
2015-12-18 14:49:31 +08:00
parent 9fde1ff772
commit f9ac065e47
106 changed files with 725 additions and 725 deletions

View File

@@ -1,7 +1,7 @@
## 11.4. 基準測試
基準測試是測量一個程序在固定工作負載下的性能. 在Go語言中, 基準測試函數和普通測試函數類似, 但是以Benchmark爲前綴名, 且帶有一個 `*testing.B` 類型的參數; `*testing.B` 除了提供和 `*testing.T` 類似的方法, 還有額外一些和性能測量相關的方法. 它還提供了一個整數N, 用於指定操作執行的循環次數.
基準測試是測量一個程序在固定工作負載下的性能. 在Go語言中, 基準測試函數和普通測試函數類似, 但是以Benchmark爲前綴名, 且帶有一個 `*testing.B` 類型的參數; `*testing.B` 除了提供和 `*testing.T` 類似的方法, 還有額外一些和性能測量相關的方法. 它還提供了一個整數N, 用於指定操作執行的循環次數.
下面是 IsPalindrome 函數的基準測試, 其中循環將執行N次.
@@ -15,7 +15,7 @@ func BenchmarkIsPalindrome(b *testing.B) {
}
```
我們用下面的命令運行基準測試. 和普通測試不同的是, 默認情況下不運行任何基準測試. 我們需要通過 `-bench` 命令行標誌參數手工指定要運行的基準測試函數. 該參數是一個正則表達式, 用於匹配要執行的基準測試函數的名字, 默認值是空的. 其中 . 模式將可以匹配所有基準測試函數, 但是這總共隻有一個基準測試函數, 因此 和 `-bench=IsPalindrome` 參數是等價的效果.
我們用下面的命令運行基準測試. 和普通測試不同的是, 默認情況下不運行任何基準測試. 我們需要通過 `-bench` 命令行標誌參數手工指定要運行的基準測試函數. 該參數是一個正則表達式, 用於匹配要執行的基準測試函數的名字, 默認值是空的. 其中 . 模式將可以匹配所有基準測試函數, 但是這總共隻有一個基準測試函數, 因此 和 `-bench=IsPalindrome` 參數是等價的效果.
```
$ cd $GOPATH/src/gopl.io/ch11/word2
@@ -25,11 +25,11 @@ 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 函數中第二個循環的停止檢査, 這樣可以避免每個比較都做兩次:
@@ -43,7 +43,7 @@ for i := 0; i < n; i++ {
return true
```
不過很多情況下, 一個明顯的優化不一定就能代碼預期的效果. 這個改進在基準測試中值帶來了 4% 的性能提.
不過很多情況下, 一個明顯的優化不一定就能代碼預期的效果. 這個改進在基準測試中值帶來了 4% 的性能提.
```
$ go test -bench=.
@@ -52,7 +52,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))
@@ -63,7 +63,7 @@ for _, r := range s {
}
```
這個改進提性能約 35%, 報告結果是基於 2,000,000 次迭代的平均運行時間統計.
這個改進提性能約 35%, 報告結果是基於 2,000,000 次迭代的平均運行時間統計.
```
$ go test -bench=.
@@ -90,9 +90,9 @@ BenchmarkIsPalindrome 2000000 807 ns/op 128 B/op 1 allocs/op
一次內存分配代替多次的內存分配節省了75%的分配調用次數和減少近一半的內存需求.
這個基準測試告訴我們所需的對時間依賴給定的具體操作, 兩個不同的操作所需時間的差異也是和不同環境相關的. 例如, 如果一個函數需要 1ms 處理 1,000 個元素, 那麽處理 10000 或 1百萬 將需要多少時間呢? 這樣的比較揭示了漸近增長函數的運行時間. 另一個例子: I/O 緩存該設置爲多大呢? 基準測試可以幫助我們選擇較小的緩存但能帶來滿意的性能. 第三個例子: 對於一個確定的工作那種算法更好? 基準測試可以評估兩種不同算法對於相同的輸入在不同的場景和負載下的優缺點.
這個基準測試告訴我們所需的對時間依賴給定的具體操作, 兩個不同的操作所需時間的差異也是和不同環境相關的. 例如, 如果一個函數需要 1ms 處理 1,000 個元素, 那麽處理 10000 或 1百萬 將需要多少時間呢? 這樣的比較揭示了漸近增長函數的運行時間. 另一個例子: I/O 緩存該設置爲多大呢? 基準測試可以幫助我們選擇較小的緩存但能帶來滿意的性能. 第三個例子: 對於一個確定的工作那種算法更好? 基準測試可以評估兩種不同算法對於相同的輸入在不同的場景和負載下的優缺點.
比較基準測試都是結構類似的代碼. 它們通常是用一個參數的函數, 從幾個標誌的基準測試函數入口調用, 就像這樣:
比較基準測試都是結構類似的代碼. 它們通常是用一個參數的函數, 從幾個標誌的基準測試函數入口調用, 就像這樣:
```Go
func benchmark(b *testing.B, size int) { /* ... */ }