mirror of
https://github.com/gopl-zh/gopl-zh.github.com.git
synced 2025-12-19 12:14:20 +08:00
rebuild
This commit is contained in:
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="11.4" data-chapter-title="基準測試" data-filepath="ch11/ch11-04.md" data-basepath=".." data-revision="Wed Dec 16 2015 10:54:29 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="11.4" data-chapter-title="基準測試" data-filepath="ch11/ch11-04.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -146,7 +146,7 @@
|
||||
|
||||
<b>0.5.</b>
|
||||
|
||||
緻謝
|
||||
致謝
|
||||
</a>
|
||||
|
||||
|
||||
@@ -212,7 +212,7 @@
|
||||
|
||||
<b>1.3.</b>
|
||||
|
||||
査找重復的行
|
||||
査找重複的行
|
||||
</a>
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@
|
||||
|
||||
<b>1.4.</b>
|
||||
|
||||
GIF動畫
|
||||
GIF動畵
|
||||
</a>
|
||||
|
||||
|
||||
@@ -257,7 +257,7 @@
|
||||
|
||||
<b>1.6.</b>
|
||||
|
||||
併髮穫取多個URL
|
||||
併發穫取多個URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -479,7 +479,7 @@
|
||||
|
||||
<b>3.3.</b>
|
||||
|
||||
復數
|
||||
複數
|
||||
</a>
|
||||
|
||||
|
||||
@@ -494,7 +494,7 @@
|
||||
|
||||
<b>3.4.</b>
|
||||
|
||||
佈爾型
|
||||
布爾型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -544,7 +544,7 @@
|
||||
|
||||
<b>4.</b>
|
||||
|
||||
復閤數據類型
|
||||
複合數據類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -857,7 +857,7 @@
|
||||
|
||||
<b>6.2.</b>
|
||||
|
||||
基於指鍼對象的方法
|
||||
基於指針對象的方法
|
||||
</a>
|
||||
|
||||
|
||||
@@ -887,7 +887,7 @@
|
||||
|
||||
<b>6.4.</b>
|
||||
|
||||
方法值和方法錶達式
|
||||
方法值和方法表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -953,7 +953,7 @@
|
||||
|
||||
<b>7.1.</b>
|
||||
|
||||
接口是閤約
|
||||
接口是合約
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1073,7 +1073,7 @@
|
||||
|
||||
<b>7.9.</b>
|
||||
|
||||
示例: 錶達式求值
|
||||
示例: 表達式求值
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1103,7 +1103,7 @@
|
||||
|
||||
<b>7.11.</b>
|
||||
|
||||
基於類型斷言識彆錯誤類型
|
||||
基於類型斷言識别錯誤類型
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1214,7 +1214,7 @@
|
||||
|
||||
<b>8.2.</b>
|
||||
|
||||
示例: 併髮的Clock服務
|
||||
示例: 併發的Clock服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1229,7 +1229,7 @@
|
||||
|
||||
<b>8.3.</b>
|
||||
|
||||
示例: 併髮的Echo服務
|
||||
示例: 併發的Echo服務
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1274,7 +1274,7 @@
|
||||
|
||||
<b>8.6.</b>
|
||||
|
||||
示例: 併髮的Web爬蟲
|
||||
示例: 併發的Web爬蟲
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1289,7 +1289,7 @@
|
||||
|
||||
<b>8.7.</b>
|
||||
|
||||
基於select的多路復用
|
||||
基於select的多路複用
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1304,7 +1304,7 @@
|
||||
|
||||
<b>8.8.</b>
|
||||
|
||||
示例: 併髮的字典遍歷
|
||||
示例: 併發的字典遍歷
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1319,7 +1319,7 @@
|
||||
|
||||
<b>8.9.</b>
|
||||
|
||||
併髮的退齣
|
||||
併發的退齣
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1354,7 +1354,7 @@
|
||||
|
||||
<b>9.</b>
|
||||
|
||||
基於共享變量的併髮
|
||||
基於共享變量的併發
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1415,7 +1415,7 @@
|
||||
|
||||
<b>9.4.</b>
|
||||
|
||||
內存衕步
|
||||
內存同步
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1460,7 +1460,7 @@
|
||||
|
||||
<b>9.7.</b>
|
||||
|
||||
示例: 併髮的非阻塞緩存
|
||||
示例: 併發的非阻塞緩存
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1475,7 +1475,7 @@
|
||||
|
||||
<b>9.8.</b>
|
||||
|
||||
Goroutines和綫程
|
||||
Goroutines和線程
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1748,7 +1748,7 @@
|
||||
|
||||
<b>12.1.</b>
|
||||
|
||||
為何需要反射?
|
||||
爲何需要反射?
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1793,7 +1793,7 @@
|
||||
|
||||
<b>12.4.</b>
|
||||
|
||||
示例: 編碼S錶達式
|
||||
示例: 編碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1823,7 +1823,7 @@
|
||||
|
||||
<b>12.6.</b>
|
||||
|
||||
示例: 解碼S錶達式
|
||||
示例: 解碼S表達式
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1975,50 +1975,14 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="exercise/ex.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
習題解答
|
||||
</a>
|
||||
|
||||
|
||||
<ul class="articles">
|
||||
|
||||
|
||||
<li class="chapter " data-level="14.1" data-path="exercise/ex-ch1.html">
|
||||
|
||||
|
||||
<a href="../exercise/ex-ch1.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.1.</b>
|
||||
|
||||
第一章 入門
|
||||
</a>
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="15" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>15.</b>
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
</a>
|
||||
@@ -2060,8 +2024,8 @@
|
||||
<section class="normal" id="section-">
|
||||
|
||||
<h2 id="114-基準測試">11.4. 基準測試</h2>
|
||||
<p>基準測試是測量一箇程序在固定工作負載下的性能. 在Go語言中, 基準測試函數和普通測試函數類似, 但是以Benchmark爲前綴名, 併且帶有一箇 <code>*testing.B</code> 類型的參數; <code>*testing.B</code> 除了提供和 <code>*testing.T</code> 類似的方法, 還有額外一些和性能測量相關的方法. 它還提供了一箇整數N, 用於指定操作執行的循環次數.</p>
|
||||
<p>下麫是 IsPalindrome 函數的基準測試, 其中循環將執行N次.</p>
|
||||
<p>基準測試是測量一個程序在固定工作負載下的性能. 在Go語言中, 基準測試函數和普通測試函數類似, 但是以Benchmark爲前綴名, 併且帶有一個 <code>*testing.B</code> 類型的參數; <code>*testing.B</code> 除了提供和 <code>*testing.T</code> 類似的方法, 還有額外一些和性能測量相關的方法. 它還提供了一個整數N, 用於指定操作執行的循環次數.</p>
|
||||
<p>下面是 IsPalindrome 函數的基準測試, 其中循環將執行N次.</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">import</span> <span class="hljs-string">"testing"</span>
|
||||
|
||||
<span class="hljs-keyword">func</span> BenchmarkIsPalindrome(b *testing.B) {
|
||||
@@ -2070,16 +2034,16 @@
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<p>我們用下麫的命令運行基準測試. 和普通測試不衕的是, 默認情況下不運行任何基準測試. 我們需要通過 <code>-bench</code> 命令行標誌參數手工指定要運行的基準測試函數. 該參數是一箇正則錶達式, 用於匹配要執行的基準測試函數的名字, 默認值是空的. 其中 ‘‘.’’ 模式將可以匹配所有基準測試函數, 但是這裡總共隻有一箇基準測試函數, 因此 和 <code>-bench=IsPalindrome</code> 參數是等價的效果.</p>
|
||||
<p>我們用下面的命令運行基準測試. 和普通測試不同的是, 默認情況下不運行任何基準測試. 我們需要通過 <code>-bench</code> 命令行標誌參數手工指定要運行的基準測試函數. 該參數是一個正則表達式, 用於匹配要執行的基準測試函數的名字, 默認值是空的. 其中 ‘‘.’’ 模式將可以匹配所有基準測試函數, 但是這里總共隻有一個基準測試函數, 因此 和 <code>-bench=IsPalindrome</code> 參數是等價的效果.</p>
|
||||
<pre><code>$ cd $GOPATH/src/gopl.io/ch11/word2
|
||||
$ go test -bench=.
|
||||
PASS
|
||||
BenchmarkIsPalindrome-8 1000000 1035 ns/op
|
||||
ok gopl.io/ch11/word2 2.179s
|
||||
</code></pre><p>基準測試名的數字後綴部分, 這裡是8, 錶示運行時對應的 GOMAXPROCS 的值, 這對於一些和併發相關的基準測試是重要的信息.</p>
|
||||
<p>報告顯示每次調用 IsPalindrome 函數花費 1.035微秒, 是執行 1,000,000 次的平均時間. 因爲基準測試驅動器併不知道每箇基準測試函數運行所花的時候, 它會嘗試在眞正運行基準測試前先嘗試用較小的 N 運行測試來估算基準測試函數所需要的時間, 然後推斷一箇較大的時間保証穩定的測量結果.</p>
|
||||
<p>循環在基準測試函數內實現, 而不是放在基準測試框架內實現, 這樣可以讓每箇基準測試函數有機會在循環啟動前執行初始化代碼, 這樣併不會顯著影響每次迭代的平均運行時間. 如果還是擔心初始化代碼部分對測量時間帶來乾擾, 那麼可以通過 testing.B 參數的方法來臨時關閉或重置計時器, 不過這些一般很少會用到.</p>
|
||||
<p>現在我們有了一箇基準測試和普通測試, 我們可以很容易測試新的讓程序運行更快的想法. 也許最明顯的優化是在 IsPalindrome 函數中第二箇循環的停止檢査, 這樣可以避免每箇比較都做兩次:</p>
|
||||
</code></pre><p>基準測試名的數字後綴部分, 這里是8, 表示運行時對應的 GOMAXPROCS 的值, 這對於一些和併發相關的基準測試是重要的信息.</p>
|
||||
<p>報告顯示每次調用 IsPalindrome 函數花費 1.035微秒, 是執行 1,000,000 次的平均時間. 因爲基準測試驅動器併不知道每個基準測試函數運行所花的時候, 它會嚐試在眞正運行基準測試前先嚐試用較小的 N 運行測試來估算基準測試函數所需要的時間, 然後推斷一個較大的時間保證穩定的測量結果.</p>
|
||||
<p>循環在基準測試函數內實現, 而不是放在基準測試框架內實現, 這樣可以讓每個基準測試函數有機會在循環啟動前執行初始化代碼, 這樣併不會顯著影響每次迭代的平均運行時間. 如果還是擔心初始化代碼部分對測量時間帶來榦擾, 那麽可以通過 testing.B 參數的方法來臨時關閉或重置計時器, 不過這些一般很少會用到.</p>
|
||||
<p>現在我們有了一個基準測試和普通測試, 我們可以很容易測試新的讓程序運行更快的想法. 也許最明顯的優化是在 IsPalindrome 函數中第二個循環的停止檢査, 這樣可以避免每個比較都做兩次:</p>
|
||||
<pre><code class="lang-Go">n := <span class="hljs-built_in">len</span>(letters)/<span class="hljs-number">2</span>
|
||||
<span class="hljs-keyword">for</span> i := <span class="hljs-number">0</span>; i < n; i++ {
|
||||
<span class="hljs-keyword">if</span> letters[i] != letters[<span class="hljs-built_in">len</span>(letters)-<span class="hljs-number">1</span>-i] {
|
||||
@@ -2088,12 +2052,12 @@ ok gopl.io/ch11/word2 2.179s
|
||||
}
|
||||
<span class="hljs-keyword">return</span> <span class="hljs-constant">true</span>
|
||||
</code></pre>
|
||||
<p>不過很多情況下, 一箇明顯的優化併不一定就能代碼預期的效果. 這箇改進在基準測試中值帶來了 4% 的性能提昇.</p>
|
||||
<p>不過很多情況下, 一個明顯的優化併不一定就能代碼預期的效果. 這個改進在基準測試中值帶來了 4% 的性能提陞.</p>
|
||||
<pre><code>$ go test -bench=.
|
||||
PASS
|
||||
BenchmarkIsPalindrome-8 1000000 992 ns/op
|
||||
ok gopl.io/ch11/word2 2.093s
|
||||
</code></pre><p>另一箇改進想法是在開始爲每箇字符預先分配一箇足夠大的數組, 這樣就可以避免在 append 調用時可能會導緻內存的多次重新分配. 聲明一箇 letters 數組變量, 併指定閤適的大小, 像這樣,</p>
|
||||
</code></pre><p>另一個改進想法是在開始爲每個字符預先分配一個足夠大的數組, 這樣就可以避免在 append 調用時可能會導致內存的多次重新分配. 聲明一個 letters 數組變量, 併指定合適的大小, 像這樣,</p>
|
||||
<pre><code class="lang-Go">letters := <span class="hljs-built_in">make</span>([]<span class="hljs-typename">rune</span>, <span class="hljs-number">0</span>, <span class="hljs-built_in">len</span>(s))
|
||||
<span class="hljs-keyword">for</span> _, r := <span class="hljs-keyword">range</span> s {
|
||||
<span class="hljs-keyword">if</span> unicode.IsLetter(r) {
|
||||
@@ -2101,12 +2065,12 @@ ok gopl.io/ch11/word2 2.093s
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<p>這箇改進提昇性能約 35%, 報告結果是基於 2,000,000 次迭代的平均運行時間統計.</p>
|
||||
<p>這個改進提陞性能約 35%, 報告結果是基於 2,000,000 次迭代的平均運行時間統計.</p>
|
||||
<pre><code>$ go test -bench=.
|
||||
PASS
|
||||
BenchmarkIsPalindrome-8 2000000 697 ns/op
|
||||
ok gopl.io/ch11/word2 1.468s
|
||||
</code></pre><p>如這箇例子所示, 快的程序往往是有很少的內存分配. <code>-benchmem</code> 命令行標誌參數將在報告中包含內存的分配數據統計. 我們可以比較優化前後內存的分配情況:</p>
|
||||
</code></pre><p>如這個例子所示, 快的程序往往是有很少的內存分配. <code>-benchmem</code> 命令行標誌參數將在報告中包含內存的分配數據統計. 我們可以比較優化前後內存的分配情況:</p>
|
||||
<pre><code>$ go test -bench=. -benchmem
|
||||
PASS
|
||||
BenchmarkIsPalindrome 1000000 1026 ns/op 304 B/op 4 allocs/op
|
||||
@@ -2115,16 +2079,16 @@ BenchmarkIsPalindrome 1000000 1026 ns/op 304 B/op 4 allocs/op
|
||||
PASS
|
||||
BenchmarkIsPalindrome 2000000 807 ns/op 128 B/op 1 allocs/op
|
||||
</code></pre><p>一次內存分配代替多次的內存分配節省了75%的分配調用次數和減少近一半的內存需求.</p>
|
||||
<p>這箇基準測試告訴我們所需的絕對時間依賴給定的具體操作, 兩箇不衕的操作所需時間的差異也是和不衕環境相關的. 例如, 如果一箇函數需要 1ms 處理 1,000 箇元素, 那麼處理 10000 或 1百萬 將需要多少時間呢? 這樣的比較揭示了漸近增長函數的運行時間. 另一箇例子: I/O 緩存該設置爲多大呢? 基準測試可以幫助我們選擇較小的緩存但能帶來滿意的性能. 第三箇例子: 對於一箇確定的工作那種算法更好? 基準測試可以評估兩種不衕算法對於相衕的輸入在不衕的場景和負載下的優缺點.</p>
|
||||
<p>比較基準測試都是結構類似的代碼. 它們通常是寀用一箇參數的函數, 從幾箇標誌的基準測試函數入口調用, 就像這樣:</p>
|
||||
<p>這個基準測試告訴我們所需的絶對時間依賴給定的具體操作, 兩個不同的操作所需時間的差異也是和不同環境相關的. 例如, 如果一個函數需要 1ms 處理 1,000 個元素, 那麽處理 10000 或 1百萬 將需要多少時間呢? 這樣的比較揭示了漸近增長函數的運行時間. 另一個例子: I/O 緩存該設置爲多大呢? 基準測試可以幫助我們選擇較小的緩存但能帶來滿意的性能. 第三個例子: 對於一個確定的工作那種算法更好? 基準測試可以評估兩種不同算法對於相同的輸入在不同的場景和負載下的優缺點.</p>
|
||||
<p>比較基準測試都是結構類似的代碼. 它們通常是采用一個參數的函數, 從幾個標誌的基準測試函數入口調用, 就像這樣:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">func</span> benchmark(b *testing.B, size <span class="hljs-typename">int</span>) { <span class="hljs-comment">/* ... */</span> }
|
||||
<span class="hljs-keyword">func</span> Benchmark10(b *testing.B) { benchmark(b, <span class="hljs-number">10</span>) }
|
||||
<span class="hljs-keyword">func</span> Benchmark100(b *testing.B) { benchmark(b, <span class="hljs-number">100</span>) }
|
||||
<span class="hljs-keyword">func</span> Benchmark1000(b *testing.B) { benchmark(b, <span class="hljs-number">1000</span>) }
|
||||
</code></pre>
|
||||
<p>通過函數參數來指定輸入的大小, 但是參數變量對於每箇具體的基準測試都是固定的. 要避免直接脩改 b.N 來控製輸入的大小. 除非你將它作爲一箇固定大小的迭代計算輸入, 否則基準測試的結果將毫無意義.</p>
|
||||
<p>基準測試對於編寫代碼是很有幫助的, 但是卽使工作完成了應應噹保存基準測試代碼. 因爲隨着項目的發展, 或者是輸入的增加, 或者是部署到新的操作繫統或不衕的處理器, 我們可以再次用基準測試來幫助我們改進設計.</p>
|
||||
<p><strong>練習 11.6:</strong> 爲 2.6.2節 的 練習 2.4 和 練習 2.5 的 PopCount 函數編寫基準測試. 看看基於錶格算法在不衕情況下的性能.</p>
|
||||
<p>通過函數參數來指定輸入的大小, 但是參數變量對於每個具體的基準測試都是固定的. 要避免直接脩改 b.N 來控製輸入的大小. 除非你將它作爲一個固定大小的迭代計算輸入, 否則基準測試的結果將毫無意義.</p>
|
||||
<p>基準測試對於編寫代碼是很有幫助的, 但是卽使工作完成了應應當保存基準測試代碼. 因爲隨着項目的發展, 或者是輸入的增加, 或者是部署到新的操作繫統或不同的處理器, 我們可以再次用基準測試來幫助我們改進設計.</p>
|
||||
<p><strong>練習 11.6:</strong> 爲 2.6.2節 的 練習 2.4 和 練習 2.5 的 PopCount 函數編寫基準測試. 看看基於表格算法在不同情況下的性能.</p>
|
||||
<p><strong>練習 11.7:</strong> 爲 *IntSet (§6.5) 的 Add, UnionWith 和 其他方法編寫基準測試, 使用大量隨機齣入. 你可以讓這些方法跑多快? 選擇字的大小對於性能的影響如何? IntSet 和基於內建 map 的實現相比有多快?</p>
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user