Go語言如何實現(xiàn)Benchmark函數(shù)
背景
go 必須要 test 才能跑benchmark,導致一些情況下想要在main函數(shù)中測試benchmark會麻煩一些,因此我實現(xiàn)了一個簡單的且沒有開銷的benchmark函數(shù),方便使用!其次也方便大家學習下如何實現(xiàn)一個零開銷的benchmark框架!
benchamrk 實現(xiàn)
對于有timeout的benchamrk,每次都去比較 time.Before(timeout) 開銷非常的大,而且 benchmark 的對外函數(shù)也不能是一個空函數(shù)一定要帶 count ,因為函數(shù)調(diào)用大概會劣化 ns 級別!
所以一般的benchmark算法都是梯度benchmark,即 1,10,100,1000,10000,100000,1000000 ... 的數(shù)量級去benchmark,好處就是避免了大量的time.Since 計算開銷,因為time.Since單次在 30ns 左右(Linux環(huán)境下),開銷非常大的!
package pprof
import (
"fmt"
"sync"
"sync/atomic"
"time"
)
func ParallelBenchmark(name string, thread int, duration time.Duration, execute func(count int)) {
wg := sync.WaitGroup{}
wg.Add(thread)
totalCount := uint64(0)
totalSpend := uint64(0)
for i := 0; i < thread; i++ {
go func() {
defer wg.Done()
spend, count := Benchmark(duration, execute)
atomic.AddUint64(&totalSpend, uint64(spend))
atomic.AddUint64(&totalCount, uint64(count))
}()
}
wg.Wait()
fmt.Printf("name=%s thread=%d duration=%s total=%d avg=%s\n", name, thread, duration, totalCount, Avg(time.Duration(totalSpend), int(totalCount)))
}
func Avg(spend time.Duration, count int) string {
avg := float64(spend) / float64(count)
if avg > 100 {
return time.Duration(avg).String()
}
return fmt.Sprintf("%.4fns", avg)
}
func Benchmark(duration time.Duration, bench func(count int)) (time.Duration, int) {
const maxTotalCount = 1000000000 // 10E
count := 1
totalSpend := time.Duration(0)
totalCount := 0
for {
start := time.Now()
bench(count)
spend := time.Since(start)
totalSpend = totalSpend + spend
totalCount = totalCount + count
if totalCount >= maxTotalCount {
break
}
subSpend := duration - totalSpend
if subSpend <= 0 {
break
}
count = totalCount*10 - totalCount
if subCount := int(float64(subSpend) / (float64(totalSpend) / float64(totalCount))); count > subCount {
count = subCount
}
}
return totalSpend, totalCount
}
profile 實現(xiàn)
package pprof
import (
"net/http"
_ "net/http/pprof"
"os"
"runtime"
"runtime/pprof"
)
// InitPProf
// go InitPProf()
func InitPProf() {
err := http.ListenAndServe(":12345", http.DefaultServeMux)
if err != nil {
panic(err)
}
}
func StartCPUProfile(fileName string) (stop func()) {
f, err := os.Create(fileName)
if err != nil {
panic(err)
}
if err := pprof.StartCPUProfile(f); err != nil {
if err := f.Close(); err != nil {
panic(err)
}
panic(err)
}
return func() {
pprof.StopCPUProfile()
if err := f.Close(); err != nil {
panic(err)
}
}
}
func StartMemProfile(fileName string) (stop func()) {
f, err := os.Create(fileName)
if err != nil {
panic(err)
}
return func() {
defer func() {
if err := f.Close(); err != nil {
panic(err)
}
}()
runtime.GC() // get up-to-date statistics
if err := pprof.WriteHeapProfile(f); err != nil {
panic(err)
}
}
}
例子
package main
import (
"github.com/anthony-dong/golang/pkg/pprof"
"sync"
"time"
)
func main() {
// 記錄 cup pprof
//stop := pprof.StartCPUProfile("cpu.out")
//defer stop()
// 并發(fā)測試 sync map的性能
mm := sync.Map{}
pprof.ParallelBenchmark("test1", 64, time.Second, func(count int) {
for i := 0; i < count; i++ {
mm.Store(i%10000, 1)
}
})
// name=test1 thread=32 duration=1s total=6708009 avg=4.772μs
// name=test1 thread=64 duration=1s total=6883456 avg=9.3μs
}到此這篇關于Go語言如何實現(xiàn)Benchmark函數(shù)的文章就介紹到這了,更多相關Go Benchmark內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
使用docker構建golang線上部署環(huán)境的步驟詳解
這篇文章主要介紹了使用docker構建golang線上部署環(huán)境的步驟,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧。2017-11-11
Golang語言中的Prometheus的日志模塊使用案例代碼編寫
這篇文章主要介紹了Golang語言中的Prometheus的日志模塊使用案例,本文給大家分享源代碼編寫方法,感興趣的朋友跟隨小編一起看看吧2024-08-08

