go benchmark 性能測試

go 性能測試

基準測試

基準測試主要是經過測試CPU和內存的效率問題,來評估被測試代碼的性能,進而找到更好的解決方案。html

編寫基準測試

func BenchmarkSprintf(b *testing.B){
	num:=10
	b.ResetTimer()
	for i:=0;i<b.N;i++{
		fmt.Sprintf("%d",num)
	}
}
  1. 基準測試的代碼文件必須以_test.go結尾
  2. 基準測試的函數必須以Benchmark開頭,必須是可導出的
  3. 基準測試函數必須接受一個指向Benchmark類型的指針做爲惟一參數
  4. 基準測試函數不能有返回值
  5. b.ResetTimer是重置計時器,這樣能夠避免for循環以前的初始化代碼的干擾
  6. 最後的for循環很重要,被測試的代碼要放到循環裏
  7. b.N是基準測試框架提供的,表示循環的次數,由於須要反覆調用測試的代碼,才能夠評估性能
➜  go test -bench=. -run=none
BenchmarkSprintf-8      20000000               117 ns/op
PASS
ok      flysnow.org/hello       2.474s

使用 go test 命令,加上 -bench= 標記,接受一個表達式做爲參數, .表示運行全部的基準測試node

由於默認狀況下 go test 會運行單元測試,爲了防止單元測試的輸出影響咱們查看基準測試的結果,可使用-run=匹配一個歷來沒有的單元測試方法,過濾掉單元測試的輸出,咱們這裏使用none,由於咱們基本上不會建立這個名字的單元測試方法。git

也可使用 -run=^$, 匹配這個規則的,可是沒有,因此只會運行benchmarkgithub

go test -bench=. -run=^$

有些時候在benchmark以前須要作一些準備工做,而且,咱們不但願這些準備工做歸入到計時裏面,咱們可使用 b.ResetTimer(),表明重置計時爲0,以調用時的時刻做爲從新計時的開始。golang

看到函數後面的-8了嗎?這個表示運行時對應的GOMAXPROCS的值。web

接着的20000000表示運行for循環的次數也就是調用被測試代碼的次數windows

最後的117 ns/op表示每次須要話費117納秒。(執行一次操做話費的時間)瀏覽器

以上是測試時間默認是1秒,也就是1秒的時間,調用兩千萬次,每次調用花費117納秒。框架

若是想讓測試運行的時間更長,能夠經過-benchtime指定,好比3秒。svg

➜  hello go test -bench=. -benchtime=3s -run=none
// Benchmark 名字 - CPU     循環次數          平均每次執行時間 
BenchmarkSprintf-8      50000000               109 ns/op
PASS
//  哪一個目錄下執行go test         累計耗時
ok      flysnow.org/hello       5.628s

能夠發現,咱們加長了測試時間,測試的次數變多了,可是最終的性能結果:每次執行的時間,並無太大變化。通常來講這個值最好不要超過3秒,意義不大。

性能對比

上面那個基準測試的例子,實際上是一個int類型轉爲string類型的例子,標準庫裏還有幾種方法,咱們看下哪一種性能更加.

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)
	}
}
➜  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

從結果上看strconv.FormatInt函數是最快的,其次是strconv.Itoa,而後是fmt.Sprintf最慢,前兩個函數性能達到了最後一個的3倍多。那麼最後一個爲何這麼慢的,咱們再經過-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能夠提供每次操做分配內存的次數,以及每次操做分配的字節數。從結果咱們能夠看到,性能高的兩個函數,每次操做都是進行1次內存分配,而最慢的那個要分配2次;性能高的每次操做分配2個字節內存,而慢的那個函數每次須要分配16字節的內存。從這個數據咱們就知道它爲何這麼慢了,內存分配都佔用都過高。

在代碼開發中,對於咱們要求性能的地方,編寫基準測試很是重要,這有助於咱們開發出性能更好的代碼。不過性能、可用性、複用性等也要有一個相對的取捨,不能爲了追求性能而過分優化。

結合 pprof

pprof 性能監控

package bench
import "testing"
func Fib(n int) int {
    if n < 2 {
      return n
    }
    return Fib(n-1) + Fib(n-2)
}
func BenchmarkFib10(b *testing.B) {
    // run the Fib function b.N times
    for n := 0; n < b.N; n++ {
      Fib(10)
    }
}
go test -bench=. -benchmem -cpuprofile profile.out

還能夠同時看內存

go test -bench=. -benchmem -memprofile memprofile.out -cpuprofile profile.out

而後就能夠用輸出的文件使用pprof

go tool pprof profile.out
File: bench.test
Type: cpu
Time: Apr 5, 2018 at 4:27pm (EDT)
Duration: 2s, Total samples = 1.85s (92.40%)
Entering interactive mode (type "help" for commands, "o" for options)
(pprof) top
Showing nodes accounting for 1.85s, 100% of 1.85s total
      flat  flat%   sum%        cum   cum%
     1.85s   100%   100%      1.85s   100%  bench.Fib
         0     0%   100%      1.85s   100%  bench.BenchmarkFib10
         0     0%   100%      1.85s   100%  testing.(*B).launch
         0     0%   100%      1.85s   100%  testing.(*B).runN

這個是使用cpu 文件, 也可使用內存文件

而後你也能夠用list命令檢查函數須要的時間

(pprof) list Fib
     1.84s      2.75s (flat, cum) 148.65% of Total
         .          .      1:package bench
         .          .      2:
         .          .      3:import "testing"
         .          .      4:
     530ms      530ms      5:func Fib(n int) int {
     260ms      260ms      6:   if n < 2 {
     130ms      130ms      7:           return n
         .          .      8:   }
     920ms      1.83s      9:   return Fib(n-1) + Fib(n-2)
         .          .     10:}

或者使用web命令生成圖像(png,pdf,...)

web

報錯:Failed to execute dot. Is Graphviz installed? Error: exec: "dot": executable file not found in %PATH%

是你電腦沒有安裝gvedit致使的

fq進入gvedit官網https://graphviz.gitlab.io/_pages/Download/Download_windows.html 下載穩定版

mac 安裝, 安裝好後就可使用web進行展示了

brew install graphviz

火焰圖

火焰圖(Flame Graph)是 Bredan Gregg 建立的一種性能分析圖表,由於它的樣子近似火焰而得名。

火焰圖 svg 文件能夠經過瀏覽器打開,它對於調用圖的最優勢是它是動態的:能夠經過點擊每一個方塊來 zoom in 分析它上面的內容。

火焰圖的調用順序從下到上,每一個方塊表明一個函數,它上面一層表示這個函數會調用哪些函數,方塊的大小表明了佔用 CPU 使用的長短。火焰圖的配色並無特殊的意義,默認的紅、黃配色是爲了更像火焰而已。

runtime/pprof分析項目, 會在當前文件夾內導出profile文件。而後用火焰圖去分析,就不能指定域名了,要指定文件。

go-torch

網上介紹大部分使用uber的開源工具

go-torch。這是 uber 開源的一個工具,能夠直接讀取 golang profiling 數據,並生成一個火焰圖的 svg 文件。

go-torch 工具的使用很是簡單,沒有任何參數的話,它會嘗試從 http://localhost:8080/debug/pprof/profile 獲取 profiling 數據。它有三個經常使用的參數能夠調整:

  • -u --url:要訪問的 URL,這裏只是主機和端口部分
  • -s --suffix:pprof profile 的路徑,默認爲 /debug/pprof/profile
  • --seconds:要執行 profiling 的時間長度,默認爲 30s

原生支持

從 Go 1.11 開始, 火焰圖被集成進入 Go 官方的 pprof 庫.

# This will listen on :8081 and open a browser.
# Change :8081 to a port of your choice.
$ go tool pprof -http=":8081" [binary] [profile]

若是低於1.11版本那麼請從git pprof

# Get the pprof tool directly
$ go get -u github.com/google/pprof

$ pprof -http=":8081" [binary] [profile]

pprof README.md

一個web 小例子

package main

import (
	"fmt"
	"log"
	"net/http"
	_ "net/http/pprof"
	"time"
)

func sayHelloHandler(w http.ResponseWriter, r *http.Request) {
	hellowold(10000)
	fmt.Println("path", r.URL.Path)
	fmt.Println("scheme", r.URL.Scheme)
	fmt.Fprintf(w, "Hello world!\n") //這個寫入到w的是輸出到客戶端的
}

func main() {
	http.HandleFunc("/", sayHelloHandler) //	設置訪問路由
	log.Fatal(http.ListenAndServe(":8080", nil))
}

func hellowold(times int) {
	time.Sleep(time.Second)
	var counter int
	for i := 0; i < times; i++ {
		for j := 0; j < times; j++ {
			counter++
		}
	}
}

使用下面的命令開啓監控,而後訪問幾回localhost:8080

go tool pprof -http=":8081" http://localhost:8080/debug/pprof/profile

過一下子會產生個web窗口, 選擇 VIEW->Flame Graph 獲得火焰圖形

http://localhost:8081/ui/flamegraph

ui

Testing flags

go 測試後面能夠跟哪些參數

Testing flags

經常使用flag

  • -bench regexp:性能測試,支持表達式對測試函數進行篩選。-bench .則是對全部的benchmark函數測試
  • -benchmem:性能測試的時候顯示測試函數的內存分配的統計信息
  • -count n:運行測試和性能多少此,默認一次
  • -run regexp:只運行特定的測試函數, 好比-run ABC只測試函數名中包含ABC的測試函數
  • -timeout t:測試時間若是超過t, panic,默認10分鐘
  • -v:顯示測試的詳細信息,也會把Log、Logf方法的日誌顯示出來

Go 1.7中開始支持 sub-test的概念。

測試注意和調優

golang性能測試與調優

  • 避免頻繁調用timer
  • 避免測試數據過大

參考

Go語言實戰筆記(二十一)| Go 單元測試

Go語言實戰筆記(二十二)| Go 基準測試

Profile your golang benchmark with pprof

深刻Go語言 - 12

Go單元測試&性能測試

golang性能測試與調優

go pprof 性能分析

go tool pprof

Go 性能測試工具PProf--火焰圖

PS: 以爲不錯的請點個贊吧!! (ง •̀_•́)ง

相關文章
相關標籤/搜索