Go性能:如何分析和加速代码

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

你的Go代码运行缓慢?是时候深入探究了

\n\n

你用Go编写了一个服务。它能运行。但当负载达到10,000 RPS时,服务器开始“喘不过气”。熟悉的情况吗?Go以其“开箱即用”的性能而闻名,但即使在这种优雅的语言中,也有其陷阱。协程泄漏、低效的内存管理、多余的分配——这些都会扼杀速度。

\n\n

在本文中,我们将探讨Go分析如何帮助找到瓶颈,并展示具体的Go应用优化技术。您将了解如何使用内置工具pprof和go test -bench将您的应用变成一台火箭。我们开始吧!

\n\n

分析器工具集:pprof和trace

\n\n

Go提供了两个强大的性能分析工具:pprof(用于分析CPU、内存、锁)和go tool trace(用于分析并发性和GC事件)。

\n\n

将pprof嵌入HTTP服务器

\n\n

开始分析的最简单方法是将net/http/pprof添加到您的服务器中。这只需要一行导入代码:

\n\n
import (\n    _ "net/http/pprof"\n    "net/http"\n)\n\nfunc main() {\n    http.ListenAndServe(":8080", nil)\n}
\n\n

现在,在地址/debug/pprof/上可以访问分析数据。要采集30秒的CPU分析数据,请执行:

\n\n
go tool pprof http://localhost:8080/debug/pprof/profile?seconds=30
\n\n

数据收集完成后,将启动一个交互式控制台。命令top15将显示最热门的函数:

\n\n
(pprof) top15\nShowing nodes accounting for 3.50s, 87.50% of 4.00s total\n      flat  flat%   sum%        cum   cum%\n     1.20s 30.00% 30.00%      1.50s 37.50%  main.heavyComputation
\n\n

看到main.heavyComputation了吗?这是我们的优化候选。要进行可视化,请使用web(如果已安装Graphviz)——您将获得一个调用图。

\n\n

内存分析:查找泄漏

\n\n

Go中的内存泄漏通常是协程泄漏或对大对象的遗忘引用。让我们采集堆分析数据:

\n\n
go tool pprof http://localhost:8080/debug/pprof/heap
\n\n

命令alloc_objects将显示哪些函数创建了最多的对象:

\n\n
(pprof) alloc_objects\n    2.50GB  100%  100%      2.50GB  100%  main.createLargeSlice
\n\n

就这样,我们找到了一个分配了2.5 GB内存的函数!可能它创建了一个巨大的切片,可以通过sync.Pool来重用。

\n\n

编写基准测试:优化的第一步

\n\n

在优化之前,需要先测量。Go基准测试是测量单个函数性能的标准工具。

\n\n

示例:比较字符串拼接

\n\n

假设我们在循环中拼接字符串。不好的做法:

\n\n
func ConcatSlow(parts []string) string {\n    result := ""\n    for _, p := range parts {\n        result += p\n    }\n    return result\n}
\n\n

好的做法是使用strings.Builder:

\n\n
func ConcatFast(parts []string) string {\n    var b strings.Builder\n    for _, p := range parts {\n        b.WriteString(p)\n    }\n    return b.String()\n}
\n\n

编写基准测试(文件concat_test.go):

\n\n
func BenchmarkConcatSlow(b *testing.B) {\n    parts := []string{"Hello", " ", "World", "!", " ", "From", " ", "Go"}\n    for i := 0; i < b.N; i++ {\n        ConcatSlow(parts)\n    }\n}\n\nfunc BenchmarkConcatFast(b *testing.B) {\n    parts := []string{"Hello", " ", "World", "!", " ", "From", " ", "Go"}\n    for i := 0; i < b.N; i++ {\n        ConcatFast(parts)\n    }\n}
\n\n

运行:

\n\n
go test -bench=. -benchmem
\n\n

结果:

\n\n
BenchmarkConc                

博客

书籍推荐