Golang使用pprof和trace進行診斷和修復性能問題
軟件開發(fā)嚴重依賴調(diào)試技術(shù),這對于有效處理性能問題至關(guān)重要。用戶在遇到程序執(zhí)行緩慢時會感到沮喪,這凸顯了通過調(diào)試工具有效識別和解決潛在問題的重要性。
但是,由于軟件的創(chuàng)建和實現(xiàn)過程中涉及龐大的代碼庫或復雜的系統(tǒng),因此調(diào)試軟件中的性能問題可能很困難。
在 Go 中,開發(fā)人員可以使用強大的內(nèi)置工具來幫助診斷和修復性能問題。其中兩個工具是 pprof
和 trace
包。
pprof
包允許您分析和分析 Go 程序的執(zhí)行,而該 trace
包允許您跟蹤和可視化事件和 goroutine
活動。當這些工具一起使用時,可以幫我們快速定位 Go 程序中導致性能低下的代碼。
了解性能問題
Go 程序或任何軟件應(yīng)用程序中的性能問題都會對用戶體驗產(chǎn)生重大影響。Go 程序中的性能問題可能由于多種原因而發(fā)生。在本節(jié)中,我們將介紹性能問題的一些最常見原因以及它們?nèi)绾斡绊懴到y(tǒng)。
- 低效算法:低效算法會對性能產(chǎn)生重大影響,尤其是在處理大型數(shù)據(jù)集時。這些算法會占用額外的 CPU 周期和內(nèi)存資源,這可能會降低整個應(yīng)用程序的速度。比如暴力搜索方法和無效的排序算法。
- 阻塞操作:應(yīng)用程序可能偶爾會等待 I/O 活動完成,例如在磁盤上讀取或?qū)懭霐?shù)據(jù)或連接到網(wǎng)絡(luò)。在此過程中,可能會發(fā)生阻塞操作并導致執(zhí)行延遲,從而導致性能下降。當應(yīng)用程序被阻塞時,它無法執(zhí)行其他有用的任務(wù),從而導致整體性能下降。
- 內(nèi)存使用率過高:使用大量內(nèi)存的 Go 應(yīng)用可能會導致性能問題,尤其是在資源不足的系統(tǒng)上。如果應(yīng)用程序消耗的內(nèi)存多于系統(tǒng)的可用內(nèi)存,則系統(tǒng)可能會開始交換到磁盤,從而大大降低應(yīng)用程序的性能。如果應(yīng)用程序不能有效地管理內(nèi)存,從而導致內(nèi)存泄漏和其他問題,也會發(fā)生這種情況。
goroutine 泄漏也會導致內(nèi)存使用率過高。另外,一些中間價比如 Elasticsearch 等,則建議直接禁用 swap,因為 swap 會導致性能下降,取而代之的是給它足夠大的內(nèi)容。
性能低下的應(yīng)用程序會導致用戶體驗差,從而導致用戶流失。為了獲得最佳體驗,優(yōu)化 Go 應(yīng)用程序至關(guān)重要。
使用 pprof 診斷性能問題
pprof
是 Go 中的一個內(nèi)置包,它為開發(fā)人員提供了一個分析工具,用于觀測他們的 Go 程序如何使用 CPU 和內(nèi)存。然后收集和分析來自此測量的數(shù)據(jù)。借助 pprof
軟件包,開發(fā)人員可以輕松測量和識別消耗比正常情況更多的 CPU 內(nèi)存的函數(shù),以及分配最多內(nèi)存的程序部分。
讓我們假設(shè)一個轉(zhuǎn)賬 App 使用 Go,并且它具有允許用戶使用二維碼向朋友匯款的功能。更新該功能后,其開發(fā)人員注意到該應(yīng)用程序的運行速度比平時慢得多,40% 的用戶抱怨掃描二維碼時延遲長達 15 秒,有時付款失敗。為了正確分析問題,開發(fā)團隊可以在用戶掃描二維碼時使用 pprof
生成 CPU 分析文件。通過分析文件,他們可能會發(fā)現(xiàn)哪些函數(shù)占用了過多的 CPU 內(nèi)存或哪些算法效率低下。在發(fā)現(xiàn)問題并修復問題后,他們可以再次測試和使用 pprof
,以確保性能得到提高,體驗更快、更無縫。
pprof 的 profile 類型
- CPU:用于分析程序的 CPU 使用情況。衡量函數(shù)如何消耗不同的 CPU 時間,從而更容易識別哪些函數(shù)消耗更多時間,這些就可能是潛在的瓶頸。
- Memory:用于分析程序的內(nèi)存使用情況。衡量應(yīng)用程序如何使用內(nèi)存以及應(yīng)用程序的哪些部分分配更多內(nèi)存。
- Block(阻塞):顯示程序阻塞的位置(例如
I/O
或同步原語),從而更容易識別并發(fā)低下的區(qū)域。 - Goroutine(協(xié)程):通過返回正在運行、阻塞和等待的狀態(tài)的 Goroutine,可以輕松檢測到并發(fā)低下的區(qū)域。
- Trace:捕獲程序執(zhí)行期間發(fā)生的事件的詳細日志,例如 goroutine 創(chuàng)建和銷毀、調(diào)度、網(wǎng)絡(luò)活動和阻塞操作。它在詳細分析應(yīng)用程序的性能時非常有用。
分析 profile
我們下面以一個例子來講解一下:
package main import ( "fmt" "math/rand" "os" "runtime/pprof" ) func main() { // 創(chuàng)建一個保存 CPU 分析結(jié)果的文件 f, err := os.Create("profile.prof") if err != nil { panic(err) } defer f.Close() // 開始采集 CPU 性能指標 if err := pprof.StartCPUProfile(f); err != nil { panic(err) } defer pprof.StopCPUProfile() // 模擬耗 CPU 的操作 for i := 0; i < 1000000; i++ { n := rand.Intn(100) _ = square(n) } } func square(n int) int { return n * n }
在上面的代碼中:
main
函數(shù)生成一個介于 1 和 1000 之間的隨機數(shù),然后計算其平方根。pprof.StartCPUProfile(f)
函數(shù)啟動 CPU 分析,從而創(chuàng)建可以在以后分析的 profile 文件。defer pprof.StopCPUProfile()
語句確保在程序結(jié)束時停止 CPU 分析,無論程序是正常終止還是由于錯誤。- 我們調(diào)用
rand.Intn(100)
1000000 次來模擬 CPU 密集型任務(wù)。
接下來,我們執(zhí)行這個程序:
go run main.go
程序運行結(jié)束后,會生成一個名為 profile.pprof
的文件,這個文件包含了 CPU 分析的數(shù)據(jù)。我們可以使用 go tool pprof
命令來分析這個文件:
go tool pprof profile.prof
接下來,會輸出如下內(nèi)容,并進入了一個交互式的命令行:
Type: cpu
Time: Jan 15, 2024 at 5:17pm (CST)
Duration: 205.21ms, Total samples = 10ms ( 4.87%)
Entering interactive mode (type "help" for commands, "o" for options)
(pprof)
我們可以接著輸入一些命令,來查看 profile 的數(shù)據(jù):
比如,我們可以輸入 top
來查看最耗 CPU 的函數(shù):
(pprof) top
Showing nodes accounting for 10ms, 100% of 10ms total
flat flat% sum% cum cum%
10ms 100% 100% 10ms 100% math/rand.(*Rand).Intn
0 0% 100% 10ms 100% main.main
0 0% 100% 10ms 100% math/rand.Intn (inline)
0 0% 100% 10ms 100% runtime.main
(pprof)
分析內(nèi)存
若要獲取內(nèi)存配置文件,請修改代碼以使用函數(shù) pprof.WriteHeapProfile()
將堆配置文件寫入文件。在生成隨機數(shù)并計算其平方后,您需要添加代碼以將內(nèi)存配置文件寫入文件(mem.prof
)。您還將添加一個 time.Sleep(5 * time.Second)
調(diào)用,以便有時間將內(nèi)存配置文件寫入文件。在下面找到代碼的更新版本:
package main import ( "fmt" "math/rand" "os" "runtime/pprof" "time" ) func main() { // 創(chuàng)建一個保存 CPU 分析結(jié)果的文件 cpuProfileFile, err := os.Create("cpu.prof") if err != nil { panic(err) } defer cpuProfileFile.Close() // 開始采集 CPU 性能指標 if err := pprof.StartCPUProfile(cpuProfileFile); err != nil { panic(err) } defer pprof.StopCPUProfile() // 模擬耗 CPU 的操作 for i := 0; i < 10; i++ { n := rand.Intn(100) s := square(n) fmt.Printf("%d^2 = %d\n", n, s) } // 創(chuàng)建一個保存內(nèi)存分析結(jié)果的文件 memProfileFile, err := os.Create("mem.prof") if err != nil { panic(err) } defer memProfileFile.Close() // 將內(nèi)存分析結(jié)果寫入文件 if err := pprof.WriteHeapProfile(memProfileFile); err != nil { panic(err) } fmt.Println("Memory profile written to mem.prof") time.Sleep(5 * time.Second) } func square(n int) int { return n * n }
輸出:
31^2 = 961
83^2 = 6889
88^2 = 7744
86^2 = 7396
14^2 = 196
99^2 = 9801
42^2 = 1764
29^2 = 841
86^2 = 7396
86^2 = 7396
Memory profile written to mem.prof
運行 go run main.go
后,將生成一個 mem.prof
文件。在交互式 shell
中,鍵入 top
以分析程序的內(nèi)存使用情況。若要顯示此交互式 shell
,請運行以下命令:
go tool pprof mem.prof
要按 CPU 使用率顯示排名靠前的函數(shù),請鍵入 top
命令:
? go tool pprof mem.prof
Type: inuse_space
Time: Jan 15, 2024 at 5:22pm (CST)
Entering interactive mode (type "help" for commands, "o" for options)
(pprof) top
Showing nodes accounting for 1.72MB, 100% of 1.72MB total
flat flat% sum% cum cum%
1.72MB 100% 100% 1.72MB 100% runtime/pprof.StartCPUProfile
0 0% 100% 1.72MB 100% main.main
0 0% 100% 1.72MB 100% runtime.main
(pprof)
從上面的示例中可以看出,pprof
可以讓我們很清楚地知道哪些函數(shù)占用了大量的內(nèi)存或者 CPU。因此,通過將 profile
納入開發(fā)過程,可以很容易地主動識別和解決性能問題,從而實現(xiàn)更快、更高效的應(yīng)用程序。
在第一個示例中,我們了解了如何使用 pprof
工具創(chuàng)建 CPU 分析文件并對其進行分析。輸出顯示每個函數(shù)的調(diào)用次數(shù),以及執(zhí)行每個函數(shù)所花費的總時間。這使我們能夠識別消耗最多 CPU 時間的函數(shù),并可能對其進行優(yōu)化。在第二個示例中,輸出顯示了每個函數(shù)的內(nèi)存使用情況,包括分配的數(shù)量和分配的字節(jié)數(shù)。這使我們能夠識別使用過多內(nèi)存的函數(shù),并可能對其進行優(yōu)化以減少內(nèi)存使用。
使用 trace 追蹤
有時,我們需要有關(guān)程序如何運行的更多詳細信息。在這種情況下,trace
包是一個非常強大和有用的工具。在本節(jié)中,我們將對其進行介紹。
trace
是一種工具,可讓您收集有關(guān)程序運行方式的詳細信息。它對于理解 goroutine
是如何創(chuàng)建和調(diào)度的、通道的使用方式以及網(wǎng)絡(luò)請求的處理方式等內(nèi)容非常有用。它提供了程序執(zhí)行的時間線視圖,可用于識別一段時間內(nèi)的性能問題和其他類型的錯誤。
trace
可以收集有關(guān)程序運行時發(fā)生的各種事件的數(shù)據(jù)。這些事件包括:Goroutine 創(chuàng)建、銷毀、阻塞、取消阻塞、網(wǎng)絡(luò)活動和垃圾回收。每個 trace 事件都分配了一個時間戳和一個 goroutine ID,允許您查看事件的順序以及它們之間的關(guān)系。
分析 trace 追蹤數(shù)據(jù)
首先,我們將創(chuàng)建一個新的 go 文件,將其命名為 trace.go
。若要生成跟蹤數(shù)據(jù),請導入 runtime/trace
包并在程序開始時調(diào)用 trace.Start
。若要停止跟蹤收集,請在程序結(jié)束時調(diào)用 trace.Stop
。下面是它的樣子:
package main import ( "fmt" "math/rand" "os" "runtime/pprof" "runtime/trace" ) func main() { // 創(chuàng)建一個保存 CPU 分析結(jié)果的文件 f, err := os.Create("profile.prof") if err != nil { panic(err) } defer f.Close() // 開始采集 CPU 性能指標 if err := pprof.StartCPUProfile(f); err != nil { panic(err) } defer pprof.StopCPUProfile() // 創(chuàng)建一個保存 trace 追蹤結(jié)果的文件 traceFile, err := os.Create("trace.out") if err != nil { panic(err) } defer traceFile.Close() if err := trace.Start(traceFile); err != nil { panic(err) } defer trace.Stop() // 模擬耗 CPU 的操作 for i := 0; i < 10; i++ { n := rand.Intn(100) _ = square(n) } } func square(n int) int { return n * n }
運行以下命令以啟動程序:
go run main.go
要分析跟蹤數(shù)據(jù),可以使用 go tool trace
命令,后跟跟蹤文件的名稱:
go tool trace trace.out
這將啟動基于 Web 的跟蹤數(shù)據(jù)可視化,您可以使用它來了解程序的運行方式并識別性能問題。
您還可以查看有關(guān)各種 goroutine 以及各種進程如何運行的詳細信息!Trace 是了解各種流事件、goroutine 分析等等的絕佳工具!
分析和修復性能問題
使用 pprof
和 trace
收集性能數(shù)據(jù)后,下一步是分析數(shù)據(jù)并確定可能的性能問題。
要解釋 pprof
的輸出,首先需要了解可用的各種類型的分析數(shù)據(jù)。最常見的配置文件類型是 CPU 和內(nèi)存配置文件,就像前面引用的示例一樣。通過分析這些配置文件,可以識別消耗大量資源并可能成為潛在瓶頸的功能。 pprof
還可以生成其他類型的配置文件,例如互斥鎖爭用和阻塞配置文件,這有助于確定同步和阻塞問題。例如,較高的互斥鎖爭用率可能表明多個 goroutine
正在爭用同一個鎖,這可能導致阻塞和性能不佳。
如前所述,跟蹤數(shù)據(jù)包含有關(guān)應(yīng)用程序行為的更全面的數(shù)據(jù),例如 goroutines、阻塞操作和網(wǎng)絡(luò)流量。跟蹤數(shù)據(jù)分析可用于檢測延遲源和其他性能問題,例如網(wǎng)絡(luò)延遲過長或選擇了效率低下的算法。
一旦確定了性能問題,有幾種方法可以優(yōu)化性能。一種常見的策略是通過重用對象來減少內(nèi)存分配,同時減少大型數(shù)據(jù)結(jié)構(gòu)的使用。通過減少可分配的內(nèi)存量和垃圾回收量,可以降低 CPU 使用率并提高整體程序性能。
另一種方法是使用異步 I/O
或非阻塞操作來減少阻塞操作,例如文件 I/O
或網(wǎng)絡(luò)通信。這有助于減少程序等待 I/O
操作完成所花費的時間,并提高整體程序吞吐量。
此外,優(yōu)化算法和數(shù)據(jù)結(jié)構(gòu)可以顯著提高性能。通過選擇更有效的算法和數(shù)據(jù)結(jié)構(gòu),可以減少完成操作所需的 CPU 時間,并提高整體程序性能。
總結(jié)
優(yōu)化 Go 應(yīng)用程序中的性能以確保它們高效且有效地運行非常重要。通過這樣做,我們可以改善用戶體驗,降低運行應(yīng)用程序的成本,并提高代碼的整體質(zhì)量。我們可以使用 pprof
和 trace
工具來分析 CPU 和內(nèi)存使用情況,并識別 Go 應(yīng)用程序中的瓶頸和其他問題。然后,我們可以根據(jù)這些工具的輸出對代碼進行有針對性的改進,例如減少內(nèi)存分配、最小化阻塞操作和優(yōu)化算法。分析工具(如 pprof
和 trace
)對于識別和解決性能問題至關(guān)重要。
以上就是Golang使用pprof和trace進行診斷和修復性能問題的詳細內(nèi)容,更多關(guān)于Go pprof trace的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
go?sync包中的互斥鎖Mutex和等待組WaitGroup使用詳解
這篇文章主要為大家介紹了go?sync包中的互斥鎖Mutex和等待組WaitGroup使用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-08-08go語言搬磚之go jmespath實現(xiàn)查詢json數(shù)據(jù)
這篇文章主要為大家介紹了go語言搬磚之go jmespath實現(xiàn)查詢json數(shù)據(jù),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-06-06