Golang基準測試

2022-01-12 仙人技術

目錄

1、基本使用

2、bench 的工作原理

3、傳入 cpu num 進行測試

4、count 多次運行基準測試

5、benchtime 指定運行秒數

6、ResetTimer 重置定時器

7、benchmem 展示內存消耗

1、基本使用

基準測試常用於代碼性能測試,函數需要導入testing包,並定義以Benchmark開頭的函數, 參數為testing.B指針類型,在測試函數中循環調用函數多次

go test testcalc/calc -bench .
go test testcalc/calc -bench . -run=none
# 顯示內存信息
go test testcalc/calc -bench . -benchmem
go test -bench=. -benchmem -run=none

go test會在運行基準測試之前之前執行包裡所有的單元測試,所有如果你的包裡有很多單元測試,或者它們會運行很長時間,你也可以通過go test的-run標識排除這些單元測試

業務代碼fib.go,測試斐波那契數列

package pkg06

func fib(n int) int {
 if n == 0 || n == 1 {
  return n
 }
 return fib(n-2) + fib(n-1)
}

測試代碼fib_test.go

package pkg06

import "testing"

func BenchmarkFib(b *testing.B) {
 for n := 0; n < b.N; n++ {
  fib(30)
 }
}

執行測試

➜  go test -bench=. -run=none
goos: darwin
goarch: amd64
pkg: pkg06
cpu: Intel(R) Core(TM) i7-8850H CPU @ 2.60GHz
BenchmarkFib-12              250           4682682 ns/op
PASS
ok      pkg06   1.875s
➜  go test -bench=. -benchmem -run=none
goos: darwin
goarch: amd64
pkg: pkg06
cpu: Intel(R) Core(TM) i7-8850H CPU @ 2.60GHz
BenchmarkFib-12              249           4686452 ns/op               0 B/op          0 allocs/op
PASS
ok      pkg06   1.854s

2、bench 的工作原理基準測試函數會被一直調用直到b.N無效,它是基準測試循環的次數b.N從1開始,如果基準測試函數在1秒內就完成 (默認值),則b.N增加,並再次運行基準測試函數b.N的值會按照序列1,2,5,10,20,50,...增加,同時再次運行基準測測試函數上述結果解讀代表1秒內運行了250次,每次4682682 ns-12後綴和用於運行次測試的GOMAXPROCS值有關。與GOMAXPROCS一樣,此數字默認為啟動時Go進程可見的CPU數。可以使用-cpu標識更改此值,可以傳入多個值以列表形式來運行基準測試3、傳入 cpu num 進行測試
➜  go test -bench=. -cpu=1,2,4  -benchmem -run=none
goos: darwin
goarch: amd64
pkg: pkg06
cpu: Intel(R) Core(TM) i7-8850H CPU @ 2.60GHz
BenchmarkFib                 244           4694667 ns/op               0 B/op          0 allocs/op
BenchmarkFib-2               255           4721201 ns/op               0 B/op          0 allocs/op
BenchmarkFib-4               256           4756392 ns/op               0 B/op          0 allocs/op
PASS
ok      pkg06   5.826s

4、count 多次運行基準測試

因為熱縮放、內存局部性、後臺處理、gc活動等等會導致單次的誤差,所以一般會進行多次測試

➜  go test -bench=. -count=10  -benchmem -run=none
goos: darwin
goarch: amd64
pkg: pkg06
cpu: Intel(R) Core(TM) i7-8850H CPU @ 2.60GHz
BenchmarkFib-12              217           5993577 ns/op               0 B/op          0 allocs/op
BenchmarkFib-12              246           5065577 ns/op               0 B/op          0 allocs/op
BenchmarkFib-12              244           4955397 ns/op               0 B/op          0 allocs/op
BenchmarkFib-12              255           4689529 ns/op               0 B/op          0 allocs/op
BenchmarkFib-12              254           4879802 ns/op               0 B/op          0 allocs/op
BenchmarkFib-12              254           4691213 ns/op               0 B/op          0 allocs/op
BenchmarkFib-12              255           4772108 ns/op               0 B/op          0 allocs/op
BenchmarkFib-12              240           4724141 ns/op               0 B/op          0 allocs/op
BenchmarkFib-12              255           4717087 ns/op               0 B/op          0 allocs/op
BenchmarkFib-12              255           4787803 ns/op               0 B/op          0 allocs/op
PASS
ok      pkg06   18.166s

5、benchtime 指定運行秒數

有的函數比較慢,為了更精確的結果,可以通過-benchtime標誌指定運行時間,從而使它運行更多次

➜  go test -bench=. -benchtime=5s  -benchmem -run=none
goos: darwin
goarch: amd64
pkg: pkg06
cpu: Intel(R) Core(TM) i7-8850H CPU @ 2.60GHz
BenchmarkFib-12             1128           4716535 ns/op               0 B/op          0 allocs/op
PASS
ok      pkg06   7.199s

6、ResetTimer 重置定時器

可能在真正測試之前還需要做很多例如初始化等工作,這時可以在需要測試的函數執行之初添加一個重置定時器的功能,這樣最終得到的時間就更為精確

package pkg06

import (
 "testing"
 "time"
)

func BenchmarkFib(b *testing.B) {
 time.Sleep(3 * time.Second)
 b.ResetTimer()
 for n := 0; n < b.N; n++ {
  fib(30)
 }
}

執行測試

➜  go test -bench=. -benchtime=5s  -benchmem -run=none
goos: darwin
goarch: amd64
pkg: pkg06
cpu: Intel(R) Core(TM) i7-8850H CPU @ 2.60GHz
BenchmarkFib-12             1239           4712413 ns/op               0 B/op          0 allocs/op
PASS
ok      pkg06   16.122s

7、benchmem 展示內存消耗例如測試大cap的切片,直接用cap初始化和cap動態擴容進行對比
package pkg07

import (
 "math/rand"
 "testing"
 "time"
)

// 指定大的cap的切片
func generateWithCap(n int) []int {
 rand.Seed(time.Now().UnixNano())
 nums := make([]int, 0, n)
 for i := 0; i < n; i++ {
  nums = append(nums, rand.Int())
 }
 return nums
}

// 動態擴容的slice
func generateDynamic(n int) []int {
 rand.Seed(time.Now().UnixNano())
 nums := make([]int, 0)
 for i := 0; i < n; i++ {
  nums = append(nums, rand.Int())
 }
 return nums
}

func BenchmarkGenerateWithCap(b *testing.B) {
 for n := 0; n < b.N; n++ {
  generateWithCap(100000)
 }
}

func BenchmarkGenerateDynamic(b *testing.B) {
 for n := 0; n < b.N; n++ {
  generateDynamic(100000)
 }
}

執行測試

➜  go test -bench=. -benchmem -run=none
goos: darwin
goarch: amd64
pkg: pkg07
cpu: Intel(R) Core(TM) i7-8850H CPU @ 2.60GHz
BenchmarkGenerateWithCap-12          672           1729465 ns/op          802817 B/op          1 allocs/op
BenchmarkGenerateDynamic-12          561           2122992 ns/op         4654346 B/op         30 allocs/op
PASS
ok      pkg07   3.777s

結論:用cap初始化好的性能可以高一個數據量級

例如測試測試函數複雜度,不帶cap的slice動態擴容

對上面代碼中調用動態擴容生成切片進行再次封裝

package pkg08

import (
 "math/rand"
 "testing"
 "time"
)

// 指定大的cap的切片
func generateWithCap(n int) []int {
 rand.Seed(time.Now().UnixNano())
 nums := make([]int, 0, n)
 for i := 0; i < n; i++ {
  nums = append(nums, rand.Int())
 }
 return nums
}

// 動態擴容的slice
func generateDynamic(n int) []int {
 rand.Seed(time.Now().UnixNano())
 nums := make([]int, 0)
 for i := 0; i < n; i++ {
  nums = append(nums, rand.Int())
 }
 return nums
}

func benchmarkGenerate(i int, b *testing.B) {
 for n := 0; n < b.N; n++ {
  generateDynamic(i)
 }
}

func BenchmarkGenerateDynamic1000(b *testing.B)     { benchmarkGenerate(1000, b) }
func BenchmarkGenerateDynamic10000(b *testing.B)    { benchmarkGenerate(10000, b) }
func BenchmarkGenerateDynamic100000(b *testing.B)   { benchmarkGenerate(100000, b) }
func BenchmarkGenerateDynamic1000000(b *testing.B)  { benchmarkGenerate(1000000, b) }
func BenchmarkGenerateDynamic10000000(b *testing.B) { benchmarkGenerate(10000000, b) }

執行測試

➜  go test -bench=. -benchmem -run=none
goos: darwin
goarch: amd64
pkg: pkg08
cpu: Intel(R) Core(TM) i7-8850H CPU @ 2.60GHz
BenchmarkGenerateDynamic1000-12            39540             26557 ns/op           16376 B/op         11 allocs/op
BenchmarkGenerateDynamic10000-12            5452            210894 ns/op          386296 B/op         20 allocs/op
BenchmarkGenerateDynamic100000-12            572           2106325 ns/op         4654341 B/op         30 allocs/op
BenchmarkGenerateDynamic1000000-12            48          23070939 ns/op        45188416 B/op         40 allocs/op
BenchmarkGenerateDynamic10000000-12            5         212567041 ns/op        423503110 B/op        50 allocs/op
PASS
ok      pkg08   9.686s

結論:輸入變為原來的10倍,單次耗時也差不多是上一級的10倍。說明這個函數的複雜度是接近線性的

See you ~

相關焦點

  • golang 基準測試和性能測試總結
    1、測試類型在*_test.go文件中有三種類型的函數,單元測試函數、基準測試函數和示例函數。
  • golang的map以及對應的基準測試
    1.基準測試(Benchmark Test)可靠的基準測試影響因素GUIDE機器必須是空閒的——不要運行在共享硬體上,在長時間運行基準測試時不要進行其他操作注意節電和熱縮放(主要指 CPU 受溫度影響導致頻率不穩定)避免虛擬機和共享雲託管; 它們太亂,無法進行一致的測量
  • Golang 性能測試 (1)
    運行結果如下圖:Golang的基準測試,默認將每個方法執行1s中,然後展示執行的次數,每一次執行的耗時, 上述還展示了內存每次分配的大小,以及每次benchmark分配的次數。上述命令指定了每個benchmark方法執行3次,並顯示內存和代碼覆蓋率。
  • golang性能優化及測試用例編寫
    前言在日常開發中,我們有時候會寫到測試用例,但是對於新手來說,並不知道測試用例該如何編寫,也不知道測試用例的用途。
  • Golang後臺單元測試實踐
    Golang單測框架選型 & 示例主要介紹golang原生testing框架、testify框架、goconvey框架,看一下哪種框架是結合業務體驗更好的。golang原生testing框架特點文件形式:文件以_test.go 結尾函數形式:func TestXxx(*testing.T)斷言:使用 t.Errorf 或相關方法來發出失敗信號運行:使用go test –v執行單元測試示例// 原函數 (in add.go)func
  • golang 編程風格最佳實踐
    /doc/effective_go.htmluber golang 代碼規範 https://github.com/uber-go/guideuber golang 代碼規範中文 https://github.com/xxjwxc/uber_go_guide_cn代碼目錄規範
  • Golang 性能分析工具簡要介紹
    接下來開始介紹比較枯燥的基礎知識了,要注意聽啦~pprof 提供了三種方式的使用Benchmark基於基準測試的 pprof,對於已經寫好的算法包來說,可以利用基準測試和 pprof 來校驗算法是否高效、內存消耗是否合理。
  • 如何基準測試 Linux PC 的性能?
    在許多情況下,「基準測試」實際上等同於「壓力測試」。通過測試硬體的極限,然後可以將測得的結果與其他硬體測得的結果作一番比較。大多數基準測試旨在模擬 PC 在實際情形下遇到的那種工作負載。正因為如此,基準測試幾乎就是獲得定量數據、了解系統性能如何的唯一方法――如果你的數據差強人意,這表明應該升級部分PC部件。[1]所以,準備好測試你自己的 PC 了嗎?
  • JMH基準測試入門配置及示例
    JMH是一個用於構建,運行和分析基於Java及其他JVM語言的基準測試工具。它也是OpenJDK項目的一部分。要運行JMH基準測試,推薦的方法是使用Maven來構建一個測試項目。生成相關的依賴信息,以及簡單的測試骨架代碼。由於這種方式比較純粹,項目是全新的、自動生成的,不受其他環境的影響,因此比較可靠。
  • Task12: 單元測試
    11.單元測試本節代碼樣例見code/utest文件夾在日常開發中,我們通常需要針對現有的功能進行單元測試,以驗證開發的正確性。在go標準庫中有一個叫做testing的測試框架,可以進行單元測試,命令是go test xxx。測試文件通常是以xx_test.go命名,放在同一包下面。
  • golang 調試分析的高階技巧
    golang 作為一門現代化語音,出生的時候就自帶完整的 debug 手段:golang tools 是直接集成在語言工具裡,支持內存分析,cpu分析,阻塞鎖分析等;delve,gdb 作為最常用的 debug 工具,讓你能夠更深入的進入程序調試;delve 當前是最友好的 golang 調試程序,ide 調試其實也是調用 dlv 而已,比如 goland;單元測試的設計深入到語言設計級別
  • Golang 調試分析的高階技巧
    golang 作為一門現代化語音,出生的時候就自帶完整的 debug 手段:golang tools 是直接集成在語言工具裡,支持內存分析,cpu分析,阻塞鎖分析等;delve,gdb 作為最常用的 debug 工具,讓你能夠更深入的進入程序調試;delve 當前是最友好的 golang 調試程序,ide 調試其實也是調用 dlv 而已,比如 goland;單元測試的設計深入到語言設計級別
  • Golang的字符編碼與regexp
    byte 是最簡單的字節類型(uint8),string 是固定長度的字節序列,其定義和初始化在 https://github.com/golang/go/blob/master/src/runtime/string.go,可以看到 string 底層就是使用 []byte 實現的:rune 類型則是 Golang 中用來處理 UTF-8 編碼的類型,實際類型為 int32,存儲的值是字符的 Unicode
  • 詳解MySQL基準測試和sysbench工具
    本文介紹了MySQL基準測試的基本概念,以及使用sysbench對MySQL進行基準測試的詳細方法。什麼是基準測試資料庫的基準測試是對資料庫的性能指標進行定量的、可復現的、可對比的測試。基準測試與壓力測試 基準測試可以理解為針對系統的一種壓力測試。但基準測試不關心業務邏輯,更加簡單、直接、易於測試,數據可以由工具生成,不要求真實;而壓力測試一般考慮業務邏輯(如購物車業務),要求真實的數據。
  • 詳解 MySQL 基準測試和 sysbench 工具
    本文介紹了MySQL基準測試的基本概念,以及使用sysbench對MySQL進行基準測試的詳細方法。文章有疏漏之處,歡迎批評指正。  一、基準測試簡介  1、什麼是基準測試資料庫的基準測試是對資料庫的性能指標進行定量的、可復現的、可對比的測試。
  • 詳解 MySQL 基準測試和sysbench工具
    基準測試與壓力測試 基準測試可以理解為針對系統的一種壓力測試。但基準測試不關心業務邏輯,更加簡單、直接、易於測試,數據可以由工具生成,不要求真實;而壓力測試一般考慮業務邏輯(如購物車業務),要求真實的數據。
  • 解析大數據基準測試——TPC-H or TPC-DS
    ;並以此為基礎,對比現有的大數據測試基準;然後重點討論TPC-DS測試基準。然而對於用戶來說,如何才能客觀地比較不同的數據管理系統,基準測試的研究也被提了出來。本文中,譚磊講解了大數據測試基準應該具有的要素,並以此為基礎對比了現有的大數據測試基準,然後重點討論TPC-DS測試基準。
  • PostgreSQL學習隨筆9 基準測試工具pgbench
    (硬體和軟體在一起)而制定的;目前有三種TPC基準規範:TPC-A,TPC-B和TPC-C;TPC-C是理事會的最新基準,TPC-A是理事會的第一個基準;其功能是制定商務應用基準程序的標準規範、性能和價格度量,並管理測試結果的發布。
  • golang標準庫template
    "text/template")type Person struct { Name string當然,也可以是一個命令,例如:計算表達式的值{{.}}、{{.Name}},或者是一個函數調用或者方法調用。可以使用管道符號|連結多個命令,用法和unix下的管道類似:|前面的命令將運算結果(或返回值)傳遞給後一個命令的最後一個位置。需要注意的是,並非只有使用了|才是pipeline。
  • 在 Node.js 中引入 Golang ,會讓它更快嗎?
    之前我也寫過一篇,在 React 項目中引入 Rust 的文章,感興趣可以看:使用 Rust 編寫更快的 React 組件最近發現了一個老外做了在 Node.js 服務中引入 Golang 的性能測試(https://blog.devgenius.io/node-js-in-go-we-trust