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-05
Go 實(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-03
Golang中struct{}和struct{}{}的區(qū)別解析
這篇文章主要介紹了Golang中struct{}和struct{}{}的區(qū)別,通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-03-03
goframe重寫FastAdmin后端實(shí)現(xiàn)實(shí)例詳解
這篇文章主要為大家介紹了goframe重寫FastAdmin后端實(shí)現(xiàn)實(shí)例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-12-12

