Golang

关注公众号 jb51net

关闭
首页 > 脚本专栏 > Golang > Go pprof trace

Golang使用pprof和trace进行诊断和修复性能问题

作者:eleven26

在 Go 中,开发人员可以使用强大的内置工具来帮助诊断和修复性能问题,其中两个工具是 pprof 和 trace 包,下面就跟随小编一起来了解下如何使用pprof和trace进行诊断和修复性能问题吧

软件开发严重依赖调试技术,这对于有效处理性能问题至关重要。用户在遇到程序执行缓慢时会感到沮丧,这凸显了通过调试工具有效识别和解决潜在问题的重要性。

但是,由于软件的创建和实现过程中涉及庞大的代码库或复杂的系统,因此调试软件中的性能问题可能很困难。

在 Go 中,开发人员可以使用强大的内置工具来帮助诊断和修复性能问题。其中两个工具是 pprof 和 trace 包。

pprof 包允许您分析和分析 Go 程序的执行,而该 trace 包允许您跟踪和可视化事件和 goroutine 活动。当这些工具一起使用时,可以帮我们快速定位 Go 程序中导致性能低下的代码。

了解性能问题

Go 程序或任何软件应用程序中的性能问题都会对用户体验产生重大影响。Go 程序中的性能问题可能由于多种原因而发生。在本节中,我们将介绍性能问题的一些最常见原因以及它们如何影响系统。

goroutine 泄漏也会导致内存使用率过高。另外,一些中间价比如 Elasticsearch 等,则建议直接禁用 swap,因为 swap 会导致性能下降,取而代之的是给它足够大的内容。

性能低下的应用程序会导致用户体验差,从而导致用户流失。为了获得最佳体验,优化 Go 应用程序至关重要。

使用 pprof 诊断性能问题

pprof 是 Go 中的一个内置包,它为开发人员提供了一个分析工具,用于观测他们的 Go 程序如何使用 CPU 和内存。然后收集和分析来自此测量的数据。借助 pprof 软件包,开发人员可以轻松测量和识别消耗比正常情况更多的 CPU 内存的函数,以及分配最多内存的程序部分。

让我们假设一个转账 App 使用 Go,并且它具有允许用户使用二维码向朋友汇款的功能。更新该功能后,其开发人员注意到该应用程序的运行速度比平时慢得多,40% 的用户抱怨扫描二维码时延迟长达 15 秒,有时付款失败。为了正确分析问题,开发团队可以在用户扫描二维码时使用 pprof 生成 CPU 分析文件。通过分析文件,他们可能会发现哪些函数占用了过多的 CPU 内存或哪些算法效率低下。在发现问题并修复问题后,他们可以再次测试和使用 pprof ,以确保性能得到提高,体验更快、更无缝。

pprof 的 profile 类型

分析 profile

我们下面以一个例子来讲解一下:

package main

import (
    "fmt"
    "math/rand"
    "os"
    "runtime/pprof"
)

func main() {
    // 创建一个保存 CPU 分析结果的文件
    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
}

在上面的代码中:

接下来,我们执行这个程序:

go run main.go

程序运行结束后,会生成一个名为 profile.pprof 的文件,这个文件包含了 CPU 分析的数据。我们可以使用 go tool pprof 命令来分析这个文件:

go tool pprof profile.prof

接下来,会输出如下内容,并进入了一个交互式的命令行:

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 的数据:

比如,我们可以输入 top 来查看最耗 CPU 的函数:

(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) 

分析内存

若要获取内存配置文件,请修改代码以使用函数 pprof.WriteHeapProfile() 将堆配置文件写入文件。在生成随机数并计算其平方后,您需要添加代码以将内存配置文件写入文件(mem.prof)。您还将添加一个 time.Sleep(5 * time.Second) 调用,以便有时间将内存配置文件写入文件。在下面找到代码的更新版本:

package main

import (
    "fmt"
    "math/rand"
    "os"
    "runtime/pprof"
    "time"
)

func main() {
    // 创建一个保存 CPU 分析结果的文件
    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)
    }

    // 创建一个保存内存分析结果的文件
    memProfileFile, err := os.Create("mem.prof")
    if err != nil {
        panic(err)
    }
    defer memProfileFile.Close()

    // 将内存分析结果写入文件
    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 以分析程序的内存使用情况。若要显示此交互式 shell,请运行以下命令:

go tool pprof mem.prof

要按 CPU 使用率显示排名靠前的函数,请键入 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 可以让我们很清楚地知道哪些函数占用了大量的内存或者 CPU。因此,通过将 profile 纳入开发过程,可以很容易地主动识别和解决性能问题,从而实现更快、更高效的应用程序。

在第一个示例中,我们了解了如何使用 pprof 工具创建 CPU 分析文件并对其进行分析。输出显示每个函数的调用次数,以及执行每个函数所花费的总时间。这使我们能够识别消耗最多 CPU 时间的函数,并可能对其进行优化。在第二个示例中,输出显示了每个函数的内存使用情况,包括分配的数量和分配的字节数。这使我们能够识别使用过多内存的函数,并可能对其进行优化以减少内存使用。

使用 trace 追踪

有时,我们需要有关程序如何运行的更多详细信息。在这种情况下,trace 包是一个非常强大和有用的工具。在本节中,我们将对其进行介绍。

trace 是一种工具,可让您收集有关程序运行方式的详细信息。它对于理解 goroutine 是如何创建和调度的、通道的使用方式以及网络请求的处理方式等内容非常有用。它提供了程序执行的时间线视图,可用于识别一段时间内的性能问题和其他类型的错误。

trace 可以收集有关程序运行时发生的各种事件的数据。这些事件包括:Goroutine 创建、销毁、阻塞、取消阻塞、网络活动和垃圾回收。每个 trace 事件都分配了一个时间戳和一个 goroutine ID,允许您查看事件的顺序以及它们之间的关系。

分析 trace 追踪数据

首先,我们将创建一个新的 go 文件,将其命名为 trace.go。若要生成跟踪数据,请导入 runtime/trace 包并在程序开始时调用 trace.Start 。若要停止跟踪收集,请在程序结束时调用 trace.Stop 。下面是它的样子:

package main

import (
    "fmt"
    "math/rand"
    "os"
    "runtime/pprof"
    "runtime/trace"
)

func main() {
    // 创建一个保存 CPU 分析结果的文件
    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()

    // 创建一个保存 trace 追踪结果的文件
    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

要分析跟踪数据,可以使用 go tool trace 命令,后跟跟踪文件的名称:

go tool trace trace.out 

这将启动基于 Web 的跟踪数据可视化,您可以使用它来了解程序的运行方式并识别性能问题。

您还可以查看有关各种 goroutine 以及各种进程如何运行的详细信息!Trace 是了解各种流事件、goroutine 分析等等的绝佳工具!

分析和修复性能问题

使用 pprof 和 trace 收集性能数据后,下一步是分析数据并确定可能的性能问题。

要解释 pprof 的输出,首先需要了解可用的各种类型的分析数据。最常见的配置文件类型是 CPU 和内存配置文件,就像前面引用的示例一样。通过分析这些配置文件,可以识别消耗大量资源并可能成为潜在瓶颈的功能。 pprof 还可以生成其他类型的配置文件,例如互斥锁争用和阻塞配置文件,这有助于确定同步和阻塞问题。例如,较高的互斥锁争用率可能表明多个 goroutine 正在争用同一个锁,这可能导致阻塞和性能不佳。

如前所述,跟踪数据包含有关应用程序行为的更全面的数据,例如 goroutines、阻塞操作和网络流量。跟踪数据分析可用于检测延迟源和其他性能问题,例如网络延迟过长或选择了效率低下的算法。

一旦确定了性能问题,有几种方法可以优化性能。一种常见的策略是通过重用对象来减少内存分配,同时减少大型数据结构的使用。通过减少可分配的内存量和垃圾回收量,可以降低 CPU 使用率并提高整体程序性能。

另一种方法是使用异步 I/O 或非阻塞操作来减少阻塞操作,例如文件 I/O 或网络通信。这有助于减少程序等待 I/O 操作完成所花费的时间,并提高整体程序吞吐量。

此外,优化算法和数据结构可以显著提高性能。通过选择更有效的算法和数据结构,可以减少完成操作所需的 CPU 时间,并提高整体程序性能。

总结

优化 Go 应用程序中的性能以确保它们高效且有效地运行非常重要。通过这样做,我们可以改善用户体验,降低运行应用程序的成本,并提高代码的整体质量。我们可以使用 pprof 和 trace 工具来分析 CPU 和内存使用情况,并识别 Go 应用程序中的瓶颈和其他问题。然后,我们可以根据这些工具的输出对代码进行有针对性的改进,例如减少内存分配、最小化阻塞操作和优化算法。分析工具(如 pprof 和 trace )对于识别和解决性能问题至关重要。

以上就是Golang使用pprof和trace进行诊断和修复性能问题的详细内容,更多关于Go pprof trace的资料请关注脚本之家其它相关文章!

您可能感兴趣的文章:
阅读全文