Golang?pprof性能測試與分析講解
一、性能分析類型
1.CPU性能分析
CPU性能分析是最常見的性能分析類型。啟動CPU分析時(shí),運(yùn)行時(shí)每隔10ms中斷一次,采集正在運(yùn)行協(xié)程的堆棧信息。
程序運(yùn)行結(jié)束后,可以根據(jù)收集的數(shù)據(jù),找到最熱代碼路徑。
一個(gè)函數(shù)在分析階段出現(xiàn)的次數(shù)越多,則該函數(shù)的代碼路徑(code path)花費(fèi)的時(shí)間占總運(yùn)行時(shí)間的比重越大。
2.內(nèi)存性能分析
內(nèi)存性能分析記錄堆內(nèi)存分配信息,忽略棧內(nèi)存的分配。
內(nèi)存分析啟動時(shí),默認(rèn)每1000次采樣1次,這個(gè)比例是可以調(diào)整的。因?yàn)閮?nèi)存性能分析是基于采樣的,因此基于內(nèi)存分析數(shù)據(jù)來判斷程序所有的內(nèi)存使用情況是很困難的。
3.阻塞性能分析
阻塞性能分析是go特點(diǎn)的。
阻塞性能分析用來記錄一個(gè)協(xié)程用來等待共享資源所花費(fèi)的時(shí)間,這用來判斷程序并發(fā)瓶頸是很有用。阻塞的場景包括:
- 在沒有緩沖的信道上發(fā)送或接受數(shù)據(jù)。
- 在空的信道上接受數(shù)據(jù)或在滿的信道上發(fā)送數(shù)據(jù)。
- 嘗試獲取一個(gè)已被其他協(xié)程占用的排他鎖。
一般情況下,當(dāng)所有的 CPU 和內(nèi)存瓶頸解決后,才會考慮這一類分析。
二、cpu性能分析
1.生成pporf
go 性能分析接口位于runtime/pprof 中:
測試代碼:生成5組數(shù)據(jù),進(jìn)行冒泡排序:
main.go
// main.go package main import ( "math/rand" "time" ) func generate(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 bubbleSort(nums []int) { for i := 0; i < len(nums); i++ { for j := 1; j < len(nums)-i; j++ { if nums[j] < nums[j-1] { nums[j], nums[j-1] = nums[j-1], nums[j] } } } } func main() { n := 10 for i := 0; i < 5; i++ { nums := generate(n) bubbleSort(nums) n *= 10 } }
想要度量這段代碼的性能,只需要在main函數(shù)最前加兩行代碼:
main()
import ( "math/rand" "os" "runtime/pprof" "time" ) func main() { pprof.StartCPUProfile(os.Stdout) defer pprof.StopCPUProfile() n := 10 for i := 0; i < 5; i++ { nums := generate(n) bubbleSort(nums) n *= 10 } }
go run main.go > cpu.pprof
當(dāng)然也可以將輸出直接導(dǎo)入到文件中:
2.分析數(shù)據(jù)
此時(shí)得到cpu.pprof 文件:
go tool pprof -http=:9999 cpu.pprof 如果提升Graphviz沒有安裝: apt installgraphviz (ubuntu)
訪問localhost:9999 得到:
除了在網(wǎng)頁中查看外,還可以使用交互式命令進(jìn)行查看:
go tool pprof cpu.pprof
使用top 查看到 bubbleSort函數(shù)占用cpu最多。
還可以使用top --cum,按照cum(累計(jì)消耗)排序:
使用help 查看幫助:
三、內(nèi)存性能分析
下面為一段字符串拼接代碼,我們對它進(jìn)行內(nèi)存分析:
package main import ( "math/rand" "github.com/pkg/profile" ) const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" func randomString(n int) string { b := make([]byte, n) for i := range b { b[i] = letterBytes[rand.Intn(len(letterBytes))] } return string(b) } func concat(n int) string { s := "" for i := 0; i < n; i++ { s += randomString(n) } return s } func main() { concat(100) }
我們使用另外一個(gè)性能分析庫"github.com/pkg/profile" 它內(nèi)部封裝了 runtime/pprof 接口,使用起來更加簡單。
cpu性能分析:
defer profile.Start().Stop()
內(nèi)存性能分析:
defer profile.Start(profile.MemProfile, profile.MemProfileRate(1)).Stop()
profile包會自動在/tmp目錄下生成profile文件
go tool pprof -http=:9999 /tmp/profile575547387/mem.pprof
可以看見concat 消耗了 524 KB, 而randomString消耗了 21KB,為什么相差這么大呢?
因?yàn)間o中的字符串不可修改,使用+ 連接字符串會導(dǎo)致重新生成新的字符串,將 + 兩邊的子字符串拷貝到新的字符串去。那這種設(shè)計(jì)多次字符串拼接的場景該如何優(yōu)化呢?使用strings.Builder
優(yōu)化后的代碼:
package main import ( "math/rand" "strings" "github.com/pkg/profile" ) const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" func randomString(n int) string { b := make([]byte, n) for i := range b { b[i] = letterBytes[rand.Intn(len(letterBytes))] } return string(b) } func concat(n int) string { sb := new(strings.Builder) for i := 0; i < n; i++ { sb.WriteString(randomString(n)) } return sb.String() } func main() { defer profile.Start(profile.MemProfile, profile.MemProfileRate(1)).Stop() concat(100) }
優(yōu)化后可以看到concat 函數(shù)使用了71KB 內(nèi)存,randomString函數(shù)使用了 21kb 內(nèi)存。
四、benchmark 生成 profile
使用benchmark 進(jìn)行基準(zhǔn)測試時(shí),除了直接查看結(jié)果,還可以生成profile
testing支持cpu、mem、block
- -cpuprofile=$FILE
- -memprofile=$FILE, -memprofilerate=N 調(diào)整記錄速率為原來的 1/N。
- -blockprofile=$FILE
fib_test.go
package fib import "testing" func fib(n int) int { if n == 0 || n == 1 { return n } return fib(n-2) + fib(n-1) } func BenchmarkFib(b *testing.B) { for n := 0; n < b.N; n++ { fib(30) // run fib(30) b.N times } }
go test -bench=. test/bench/fib -cpuprofile=cpu.pprof
go tool pprof -test cpu.pprof
go tool pprof 支持多種輸出格式:
go tool pprof
到此這篇關(guān)于Golang pprof性能測試與分析講解的文章就介紹到這了,更多相關(guān)Go pprof性能測試內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
GO 使用Webhook 實(shí)現(xiàn)github 自動化部署的方法
這篇文章主要介紹了GO 使用Webhook 實(shí)現(xiàn)github 自動化部署的方法,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-05-05Go 實(shí)現(xiàn)基于Token 的登錄流程深度分析
Token 認(rèn)證機(jī)制的核心思想是,服務(wù)端在用戶登錄時(shí)生成一個(gè) Token,客戶端在后續(xù)的請求中攜帶這個(gè) Token,服務(wù)端通過驗(yàn)證 Token 的有效性來確認(rèn)用戶的身份,本文將帶你深入探索基于 Token 的登錄流程,這是一種更為靈活且適用于現(xiàn)代應(yīng)用架構(gòu)的認(rèn)證方式2024-03-03Golang中struct{}和struct{}{}的區(qū)別解析
這篇文章主要介紹了Golang中struct{}和struct{}{}的區(qū)別,通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-03-03goframe重寫FastAdmin后端實(shí)現(xiàn)實(shí)例詳解
這篇文章主要為大家介紹了goframe重寫FastAdmin后端實(shí)現(xiàn)實(shí)例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-12-12