Go語(yǔ)言如何實(shí)現(xiàn)Benchmark函數(shù)
背景
go 必須要 test 才能跑benchmark,導(dǎo)致一些情況下想要在main函數(shù)中測(cè)試benchmark會(huì)麻煩一些,因此我實(shí)現(xiàn)了一個(gè)簡(jiǎn)單的且沒有開銷的benchmark函數(shù),方便使用!其次也方便大家學(xué)習(xí)下如何實(shí)現(xiàn)一個(gè)零開銷的benchmark框架!
benchamrk 實(shí)現(xiàn)
對(duì)于有timeout的benchamrk,每次都去比較 time.Before(timeout) 開銷非常的大,而且 benchmark 的對(duì)外函數(shù)也不能是一個(gè)空函數(shù)一定要帶 count ,因?yàn)楹瘮?shù)調(diào)用大概會(huì)劣化 ns 級(jí)別!
所以一般的benchmark算法都是梯度benchmark,即 1,10,100,1000,10000,100000,1000000 ... 的數(shù)量級(jí)去benchmark,好處就是避免了大量的time.Since 計(jì)算開銷,因?yàn)閠ime.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 實(shí)現(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ā)測(cè)試 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 }
到此這篇關(guān)于Go語(yǔ)言如何實(shí)現(xiàn)Benchmark函數(shù)的文章就介紹到這了,更多相關(guān)Go Benchmark內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
詳解Go語(yǔ)言如何使用標(biāo)準(zhǔn)庫(kù)sort對(duì)切片進(jìn)行排序
Sort?標(biāo)準(zhǔn)庫(kù)提供了對(duì)基本數(shù)據(jù)類型的切片和自定義類型的切片進(jìn)行排序的函數(shù)。今天主要分享的內(nèi)容是使用?Go?標(biāo)準(zhǔn)庫(kù)?sort?對(duì)切片進(jìn)行排序,感興趣的可以了解一下2022-12-12Go語(yǔ)言并發(fā)之context標(biāo)準(zhǔn)庫(kù)的使用詳解
Context的出現(xiàn)是為了解決在大型應(yīng)用程序中的并發(fā)環(huán)境下,協(xié)調(diào)和管理多個(gè)goroutine之間的通信、超時(shí)和取消操作的問題,本文就來(lái)和大家簡(jiǎn)單聊聊它的具體用法,希望對(duì)大家有所幫助2023-06-06Golang實(shí)現(xiàn)超時(shí)機(jī)制讀取文件的方法示例
讀寫文件是Go程序的基本任務(wù),包括使用程序查看文件內(nèi)容、創(chuàng)建或修改文件,Go提供了os,ioutil,io以及bufio包實(shí)現(xiàn)文件操作,本文介紹如果在讀文件過(guò)程中增加超時(shí)機(jī)制,避免文件太大一直占用資源,需要的朋友可以參考下2025-01-01使用docker構(gòu)建golang線上部署環(huán)境的步驟詳解
這篇文章主要介紹了使用docker構(gòu)建golang線上部署環(huán)境的步驟,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。2017-11-11Go語(yǔ)言使用組合的方式實(shí)現(xiàn)多繼承的方法
這篇文章主要介紹了Go語(yǔ)言使用組合的方式實(shí)現(xiàn)多繼承的方法,實(shí)例分析了多繼承的原理與使用組合方式來(lái)實(shí)現(xiàn)多繼承的技巧,需要的朋友可以參考下2015-02-02Golang語(yǔ)言中的Prometheus的日志模塊使用案例代碼編寫
這篇文章主要介紹了Golang語(yǔ)言中的Prometheus的日志模塊使用案例,本文給大家分享源代碼編寫方法,感興趣的朋友跟隨小編一起看看吧2024-08-08如何go語(yǔ)言比較兩個(gè)對(duì)象是否深度相同
這篇文章主要介紹了如何go語(yǔ)言比較兩個(gè)對(duì)象是否深度相同,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下2022-05-05