百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 热门文章 > 正文

爱上开源之golang入门至实战第三章-CPU Profiling数据剖析

bigegpt 2024-08-20 11:09 3 浏览

3.4.4 性能分析

我们使用上面的两种方法采集到运行的golang程序的性能数据样本后,下面就可以用我们第一步,安装的pprof工具来查看性能数据了, pprof可视化分析工具,主要是对样本数据文件进行解析,提供解析后的性能数据给我查看。

现在我们通过一个实例来详细的介绍一下性能分析的过程。 通过pprof的来查看笔者的一款基于fasthttp服务的性能数据

由于是一个非http底层的服务,fasthttp本身有兼容pprof的实现方式,但是笔者简单的还是通过另其一个单独的http服务来实现;仅仅只是为了演示而已;在真实的场景里,还是应该在成本可控的范围内,仅可能不要过多的启用冗余的底层服务和端口,

解释

对于云原生或者容器开发而言,多一个底层服务可能就需要至少publish出一个服务端口,这对于云原生或者容器开发而言,都是不必要的资源浪费,而且还带来了维护和管理上的不必要的管理内容

Pprof代码,简单的引入包和启动默认Mux

import _ "net/http/pprof"
go http.ListenAndServe("0.0.0.0:8889", nil)

查看pprof的性能数据

http://localhost:8999/debug/pprof/



在PProf的性能采样报告里,包含以下方面的数据样本提供pprof工具进行可视化分析

profile CPU占用情况的采样信息 allocs 内存分配情况的采样信息 blocks 阻塞操作情况的采样信息 cmdline 显示程序启动命令及参数 goroutine 当前所有协程的堆栈信息 heap 堆上内存使用情况的采样信息 mutex 锁争用情况的采样信息 threadcreate 系统线程创建情况的采样信息 trace 程序运行跟踪信息


提示

这里演示的是后台服务类型的程序,通过http的方式提供性能数据样本来源, 如果是Run over once运行程序, 可以将样本数据通过pprof提供的API保存到文件里,然后通过pprof工具进行可视化查看和分析

使用go tool pprof分析数据命令: go tool pprof pprof_file|pprof_url 进入命令行交互模式后,可以使用help查看所有子命令,使用help <cmd|option>查看子命令使用方法。

Duration: 30s, Total samples = 20ms (0.067%) Entering interactive mode (type "help" for commands, "o" for options) (pprof) help Commands: callgrind Outputs a graph in callgrind format comments Output all profile comments disasm Output assembly listings annotated with samples dot Outputs a graph in DOT format eog Visualize graph through eog evince Visualize graph through evince gif Outputs a graph image in GIF format gv Visualize graph through gv kcachegrind Visualize report in KCachegrind list Output annotated source for functions matching regexp pdf Outputs a graph in PDF format


CPU Profiling

运行命令

go tool pprof http://localhost:8999/debug/pprof/profile


采样持续时间30s, 使用top命令,查看最耗时的地方


Top命令的结果,列表出了在采样的持续时间30s内,发生的程序中各种函数调用过程中最耗时的前n条信息;

其中每一行都表示一个采用到的函数调用,

每类都对应着采集到的数据分析; 列的参照如下

flat:函数在 CPU 上运行的时间

flat%:函数在CPU上运行时间的百分比

sum%:所有函数累加使用 CPU 的比例,即所有flat%的总和

cum: 函数以及子函数运行所占用的时间,应该大于等于flat

cum%: 函数以及子函数运行所占用的比例,应该大于等于flat%

函数的名字

通过上面的报告,我们可以看到,在采样的30s的时间内, 程序基本上没有发生调用。 而且根据所有相关程序调用的信息,这两个10ms耗时的调用,还是和pprof采样有关的,不过对性能的开销基本上可以忽略不计(10ms/30s);

结合着压力工具,对服务进行加压, 这时候就可以看到加压过程中的各种耗时函数调用的情况了, 在笔者演示的这个项目里,在前台有个定时刷新的任务,打开这个任务没5s刷新; 同时采样的持续时间改成60s, 然后查看采样数据的变化

采用持续时间改成60s

E:\WORK\PROJECT\git\go\golang-sample\Demo> go tool pprof http://localhost:8999/debug/pprof/profile?seconds=60


查看前20条耗时最长的调用


这里可以看到调用现在最长的耗时0.79s,runtime.cgocall, 然后就可以通过提供的这些函数名来对自己的程序进行分析和研判了

使用子命令:list 函数名。 可以用pprof分析函数中的哪一行导致的耗时

(pprof) list runtime.cgocall
Total: 1.02s
ROUTINE ======================== runtime.cgocall in E:\WORK\SOFT\go1.18.windows-amd64\go\src\runtime\cgocall.go
     790ms      790ms (flat, cum) 77.45% of Total
         .          .    152:   //
         .          .    153:   // fn may call back into Go code, in which case we'll exit the
         .          .    154:   // "system call", run the Go code (which may grow the stack),
         .          .    155:   // and then re-enter the "system call" reusing the PC and SP
         .          .    156:   // saved by entersyscall here.
     790ms      790ms    157:   entersyscall()
         .          .    158:
         .          .    159:   // Tell asynchronous preemption that we're entering external
         .          .    160:   // code. We do this after entersyscall because this may block
         .          .    161:   // and cause an async preemption to fail, but at this point a
         .          .    162:   // sync preemption will succeed (though this is not a matter

通过这个命令,可以直接追击到源代码的行数,从而进行仔细的调用过程分析

这里我们根据分析看到的,打开cgocall.go源文件,一探究竟

技巧

在Idea里,使用快捷键 Ctrl + Shift + R ; 然后在搜索框里,输入 cgocall.go,就可以快速的定位到cgocall.go文件


    //
    // fn may call back into Go code, in which case we'll exit the
    // "system call", run the Go code (which may grow the stack),
    // and then re-enter the "system call" reusing the PC and SP
    // saved by entersyscall here.
    entersyscall()

至这个地方,可以看到的是go底层的syscall的系统调用; 虽然现在的问题耗时点最长的是他,但是目前的调用他并不算太多,采样时长是60s, 目前他的耗时是0.79s(790ms 77.45); 我们还是继续看看是否是我们的主要业务代码导致的;

Graphviz图形化,查看调用关系

如果安装了graphviz工具, 还可以通过图形化的方式, 来查看每个函数的调用关系图, 在pprof的交互模式里输入web命令

(pprof) help web
Visualize graph through web browser
  Usage:
    web [n] [focus_regex]* [-ignore_regex]*
    Include up to n samples
    Include samples matching focus_regex, and exclude ignore_regex.

执行命令后,pprof会根据样本数据产生一个svg的图像文件,并自动启动浏览器打开该文件,显示出各个函数的调用关系


每个方形格子就是一个函数调用, 格子越大,耗时就越长,可以放大图片,看到每个格子的具体的调用数据



查看runtime.cgocall的调用链, 可以看到耗时最长的这个调用链,是0.42s,如下图



继续往这条链上溯源下去,发现0.43的主要分支0.26S,0.14S, 0.03S, 可以分别在图里找到其调用的起源,

  1. 0.26s的调用来源于Log4go的日志功能,在图中可以看到log4go在这里分别有两中调用,一种来自于ConsoleLoggerAppender,其直接使用fmt进行打印控制操作,这个调用链可以看到fmt的性能真的是不好,所有的耗时0.23全部在这个函数里面, 在log4go里考虑到fmt糟糕的性能,都是使用的异步输出,所有这里我们看到的耗时是0.23,但是程序的表现上,是异步的,还是没有太多的视觉上的差异, log4go这里还有另一个FileLoggerAppender,日志输出至文件的,整个调用链耗时0.04s, 比fm优秀太多
  2. 0.14s的调用来源,也全部都是fmt调用链占用的, 调用来源于两处, 0.08s和0.05s, 并不是差了0.01s,是报告显示的时候,使用了2位小数的四舍五入,所以有这个差异
  3. 0.03的调用来源,图中没有展示全, 来之于业务底层框架上的调用,这个可以进一步追击下去了,和业务相关,今天笔者就不在这里进行深入下去了。



如果没有安装好graphviz工具,而不能使用web命令打开如上面这样的图形页面时; 可以使用pprof的tree命令; tree命令,也可以查看到整个调用过程中函数的调用关系的结构图,只是没有graphviz这么直观而已

(pprof) tree
Showing nodes accounting for 160ms, 100% of 160ms total                                                                
----------------------------------------------------------+-------------                                               
      flat  flat%   sum%        cum   cum%   calls calls% + context                                                    
----------------------------------------------------------+-------------                                               
                                             100ms   100% |   syscall.SyscallN                                         
     100ms 62.50% 62.50%      100ms 62.50%                | runtime.cgocall                                            
----------------------------------------------------------+-------------                                               
                                              10ms   100% |   github.com/valyala/fasthttp.(*RequestHeader).parseHeaders
      10ms  6.25% 68.75%       10ms  6.25%                | bytes.IndexByte                                            
----------------------------------------------------------+-------------                                               
                                              10ms   100% |   github.com/valyala/fasthttp.(*RequestHeader).parseHeaders
      10ms  6.25% 75.00%       10ms  6.25%                | github.com/valyala/fasthttp.caseInsensitiveCompare         
----------------------------------------------------------+-------------                                               
                                              10ms   100% |   runtime.mallocgc                                         
      10ms  6.25% 81.25%       10ms  6.25%                | runtime.memclrNoHeapPointers                               
----------------------------------------------------------+-------------                                               
                                              10ms   100% |   time.Time.AppendFormat                                   
      10ms  6.25% 87.50%       10ms  6.25%                | runtime.memmove                                            
----------------------------------------------------------+-------------                                               
                                              20ms   100% |   runtime.resettimer
      10ms  6.25% 93.75%       20ms 12.50%                | runtime.modtimer
                                              10ms 50.00% |   runtime.wakeNetPoller
----------------------------------------------------------+-------------
                                              10ms   100% |   runtime.semawakeup
      10ms  6.25%   100%       10ms  6.25%                | runtime.stdcall1
----------------------------------------------------------+-------------
                                              40ms   100% |   github.com/gohutool/log4go.(*FileLoggerAppender).Start.func1
         0     0%   100%       40ms 25.00%                | fmt.Fprint
                                              40ms   100% |   os.(*File).Write


技巧

top n 返回前n条信息

采样的持续时间,可以在执行go tool pprof url地址后面加上查询参数 ?seconds=n, 例如 go tool pprof http://localhost:8999/debug/pprof/profile?seconds=10




相关推荐

悠悠万事,吃饭为大(悠悠万事吃饭为大,什么意思)

新媒体编辑:杜岷赵蕾初审:程秀娟审核:汤小俊审签:周星...

高铁扒门事件升级版!婚宴上‘冲喜’老人团:我们抢的是社会资源

凌晨两点改方案时,突然收到婚庆团队发来的视频——胶东某酒店宴会厅,三个穿大红棉袄的中年妇女跟敢死队似的往前冲,眼瞅着就要扑到新娘的高额钻石项链上。要不是门口小伙及时阻拦,这婚礼造型团队熬了三个月的方案...

微服务架构实战:商家管理后台与sso设计,SSO客户端设计

SSO客户端设计下面通过模块merchant-security对SSO客户端安全认证部分的实现进行封装,以便各个接入SSO的客户端应用进行引用。安全认证的项目管理配置SSO客户端安全认证的项目管理使...

还在为 Spring Boot 配置类加载机制困惑?一文为你彻底解惑

在当今微服务架构盛行、项目复杂度不断攀升的开发环境下,SpringBoot作为Java后端开发的主流框架,无疑是我们手中的得力武器。然而,当我们在享受其自动配置带来的便捷时,是否曾被配置类加载...

Seata源码—6.Seata AT模式的数据源代理二

大纲1.Seata的Resource资源接口源码2.Seata数据源连接池代理的实现源码3.Client向Server发起注册RM的源码4.Client向Server注册RM时的交互源码5.数据源连接...

30分钟了解K8S(30分钟了解微积分)

微服务演进方向o面向分布式设计(Distribution):容器、微服务、API驱动的开发;o面向配置设计(Configuration):一个镜像,多个环境配置;o面向韧性设计(Resista...

SpringBoot条件化配置(@Conditional)全面解析与实战指南

一、条件化配置基础概念1.1什么是条件化配置条件化配置是Spring框架提供的一种基于特定条件来决定是否注册Bean或加载配置的机制。在SpringBoot中,这一机制通过@Conditional...

一招解决所有依赖冲突(克服依赖)

背景介绍最近遇到了这样一个问题,我们有一个jar包common-tool,作为基础工具包,被各个项目在引用。突然某一天发现日志很多报错。一看是NoSuchMethodError,意思是Dis...

你读过Mybatis的源码?说说它用到了几种设计模式

学习设计模式时,很多人都有类似的困扰——明明概念背得滚瓜烂熟,一到写代码就完全想不起来怎么用。就像学了一堆游泳技巧,却从没下过水实践,很难真正掌握。其实理解一个知识点,就像看立体模型,单角度观察总...

golang对接阿里云私有Bucket上传图片、授权访问图片

1、为什么要设置私有bucket公共读写:互联网上任何用户都可以对该Bucket内的文件进行访问,并且向该Bucket写入数据。这有可能造成您数据的外泄以及费用激增,若被人恶意写入违法信息还可...

spring中的资源的加载(spring加载原理)

最近在网上看到有人问@ContextConfiguration("classpath:/bean.xml")中除了classpath这种还有其他的写法么,看他的意思是想从本地文件...

Android资源使用(android资源文件)

Android资源管理机制在Android的开发中,需要使用到各式各样的资源,这些资源往往是一些静态资源,比如位图,颜色,布局定义,用户界面使用到的字符串,动画等。这些资源统统放在项目的res/独立子...

如何深度理解mybatis?(如何深度理解康乐服务质量管理的5个维度)

深度自定义mybatis回顾mybatis的操作的核心步骤编写核心类SqlSessionFacotryBuild进行解析配置文件深度分析解析SqlSessionFacotryBuild干的核心工作编写...

@Autowired与@Resource原理知识点详解

springIOCAOP的不多做赘述了,说下IOC:SpringIOC解决的是对象管理和对象依赖的问题,IOC容器可以理解为一个对象工厂,我们都把该对象交给工厂,工厂管理这些对象的创建以及依赖关系...

java的redis连接工具篇(java redis client)

在Java里,有不少用于连接Redis的工具,下面为你介绍一些主流的工具及其特点:JedisJedis是Redis官方推荐的Java连接工具,它提供了全面的Redis命令支持,且...