Go语言核心36讲50
作为拾遗的部分,今天我们来讲讲与Go程序性能分析有关的基础知识。
Go语言为程序开发者们提供了丰富的性能分析API,和非常好用的标准工具。这些API主要存在于:
runtime/pprof
;net/http/pprof
;runtime/trace
;
这三个代码包中。
另外,runtime
代码包中还包含了一些更底层的API。它们可以被用来收集或输出Go程序运行过程中的一些关键指标,并帮助我们生成相应的概要文件以供后续分析时使用。
至于标准工具,主要有go tool pprof
和go tool trace
这两个。它们可以解析概要文件中的信息,并以人类易读的方式把这些信息展示出来。
此外,go test
命令也可以在程序测试完成后生成概要文件。如此一来,我们就可以很方便地使用前面那两个工具读取概要文件,并对被测程序的性能加以分析。这无疑会让程序性能测试的一手资料更加丰富,结果更加精确和可信。
在Go语言中,用于分析程序性能的概要文件有三种,分别是:CPU概要文件(CPU Profile)、内存概要文件(Mem Profile)和阻塞概要文件(Block Profile)。
这些概要文件中包含的都是:在某一段时间内,对Go程序的相关指标进行多次采样后得到的概要信息。
对于CPU概要文件来说,其中的每一段独立的概要信息都记录着,在进行某一次采样的那个时刻,CPU上正在执行的Go代码。
而对于内存概要文件,其中的每一段概要信息都记载着,在某个采样时刻,正在执行的Go代码以及堆内存的使用情况,这里包含已分配和已释放的字节数量和对象数量。至于阻塞概要文件,其中的每一段概要信息,都代表着Go程序中的一个goroutine阻塞事件。
注意,在默认情况下,这些概要文件中的信息并不是普通的文本,它们都是以二进制的形式展现的。如果你使用一个常规的文本编辑器查看它们的话,那么肯定会看到一堆“乱码”。
这时就可以显现出go tool pprof
这个工具的作用了。我们可以通过它进入一个基于命令行的交互式界面,并对指定的概要文件进行查阅。就像下面这样:
$ go tool pprof cpuprofile.out
Type: cpu
Time: Nov 9, 2018 at 4:31pm (CST)
Duration: 7.96s, Total samples = 6.88s (86.38%)
Entering interactive mode (type "help" for commands, "o" for options)
(pprof)
关于这个工具的具体用法,我就不在这里赘述了。在进入这个工具的交互式界面之后,我们只要输入指令help
并按下回车键,就可以看到很详细的帮助文档。
我们现在来说说怎样生成概要文件。
你可能会问,既然在概要文件中的信息不是普通的文本,那么它们到底是什么格式的呢?一个对广大的程序开发者而言,并不那么重要的事实是,它们是通过protocol buffers生成的二进制数据流,或者说字节流。
概括来讲,protocol buffers是一种数据序列化协议,同时也是一个序列化工具。它可以把一个值,比如一个结构体或者一个字典,转换成一段字节流。
也可以反过来,把经过它生成的字节流反向转换为程序中的一个值。前者就被叫做序列化,而后者则被称为反序列化。
换句话说,protocol buffers定义和实现了一种“可以让数据在结构形态和扁平形态之间互相转换”的方式。
Protocol buffers的优势有不少。比如,它可以在序列化数据的同时对数据进行压缩,所以它生成的字节流,通常都要比相同数据的其他格式(例如XML和JSON)占用的空间明显小很多。
又比如,它既能让我们自己去定义数据序列化和结构化的格式,也允许我们在保证向后兼容的前提下去更新这种格式。
正因为这些优势,Go语言从1.8版本开始,把所有profile相关的信息生成工作都交给protocol buffers来做了。这也是我们在上述概要文件中,看不到普通文本的根本原因了。
Protocol buffers的用途非常广泛,并且在诸如数据存储、数据传输等任务中有着很高的使用率。不过,关于它,我暂时就介绍到这里。你目前知道这些也就足够了。你并不用关心runtime/pprof
包以及runtime
包中的程序是如何序列化这些概要信息的。
继续回到怎样生成概要文件的话题,我们依然通过具体的问题来讲述。
我们今天的问题是:怎样让程序对CPU概要信息进行采样?
这道题的典型回答是这样的。
这需要用到runtime/pprof
包中的API。更具体地说,在我们想让程序开始对CPU概要信息进行采样的时候,需要调用这个代码包中的StartCPUProfile
函数,而在停止采样的时候则需要调用该包中的StopCPUProfile
函数。
问题解析
runtime/pprof.StartCPUProfile
函数(以下简称StartCPUProfile
函数)在被调用的时候,先会去设定CPU概要信息的采样频率,并会在单独的goroutine中进行CPU概要信息的收集和输出。
注意,StartCPUProfile
函数设定的采样频率总是固定的,即:100
赫兹。也就是说,每秒采样100
次,或者说每10
毫秒采样一次。
赫兹,也称Hz,是从英文单词“Hertz”(一个英文姓氏)音译过来的一个中文词。它是CPU主频的基本单位。
CPU的主频指的是,CPU内核工作的时钟频率,也常被称为CPU clock speed。这个时钟频率的倒数即为时钟周期(clock cycle),也就是一个CPU内核执行一条运算指令所需的时间,单位是秒。
例如,主频为1000
Hz的CPU,它的单个内核执行一条运算指令所需的时间为0.001
秒,即1
毫秒。又例如,我们现在常用的3.2
GHz的多核CPU,其单个内核在1
个纳秒的时间里就可以至少执行三条运算指令。
StartCPUProfile
函数设定的CPU概要信息采样频率,相对于现代的CPU主频来说是非常低的。这主要有两个方面的原因。
一方面,过高的采样频率会对Go程序的运行效率造成很明显的负面影响。因此,runtime
包中SetCPUProfileRate
函数在被调用的时候,会保证采样频率不超过1
MHz(兆赫),也就是说,它只允许每1
微秒最多采样一次。StartCPUProfile
函数正是通过调用这个函数来设定CPU概要信息的采样频率的。
另一方面,经过大量的实验,Go语言团队发现100
Hz是一个比较合适的设定。因为这样做既可以得到足够多、足够有用的概要信息,又不至于让程序的运行出现停滞。另外,操作系统对高频采样的处理能力也是有限的,一般情况下,超过500
Hz就很可能得不到及时的响应了。
在StartCPUProfile
函数执行之后,一个新启用的goroutine将会负责执行CPU概要信息的收集和输出,直到runtime/pprof
包中的StopCPUProfile
函数被成功调用。
StopCPUProfile
函数也会调用runtime.SetCPUProfileRate
函数,并把参数值(也就是采样频率)设为0
。这会让针对CPU概要信息的采样工作停止。
同时,它也会给负责收集CPU概要信息的代码一个“信号”,以告知收集工作也需要停止了。
在接到这样的“信号”之后,那部分程序将会把这段时间内收集到的所有CPU概要信息,全部写入到我们在调用StartCPUProfile
函数的时候指定的写入器中。只有在上述操作全部完成之后,StopCPUProfile
函数才会返回。
好了,经过这一番解释,你应该已经对CPU概要信息的采样工作有一定的认识了。你可以去看看demo96.go文件中的代码,并运行几次试试。这样会有助于你加深对这个问题的理解。
总结
我们这两篇内容讲的是Go程序的性能分析,这其中的内容都是你从事这项任务必备的一些知识和技巧。
首先,我们需要知道,与程序性能分析有关的API主要存在于runtime
、runtime/pprof
和net/http/pprof
这几个代码包中。它们可以帮助我们收集相应的性能概要信息,并把这些信息输出到我们指定的地方。
Go语言的运行时系统会根据要求对程序的相关指标进行多次采样,并对采样的结果进行组织和整理,最后形成一份完整的性能分析报告。这份报告就是我们一直在说的概要信息的汇总。
一般情况下,我们会把概要信息输出到文件。根据概要信息的不同,概要文件的种类主要有三个,分别是:CPU概要文件(CPU Profile)、内存概要文件(Mem Profile)和阻塞概要文件(Block Profile)。
在本文中,我提出了一道与上述几种概要信息有关的问题。在下一篇文章中,我们会继续对这部分问题的探究。
你对今天的内容有什么样的思考与疑惑,可以给我留言,感谢你的收听,我们下次再见。
Go语言核心36讲50的更多相关文章
- Go语言核心36讲(Go语言实战与应用十八)--学习笔记
40 | io包中的接口和工具 (上) 我们在前几篇文章中,主要讨论了strings.Builder.strings.Reader和bytes.Buffer这三个数据类型. 知识回顾 还记得吗?当时我 ...
- Go语言核心36讲(导读)--学习笔记
目录 开篇词 | 跟着学,你也能成为Go语言高手 导读 | 写给0基础入门的Go语言学习者 导读 | 学习专栏的正确姿势 开篇词 | 跟着学,你也能成为Go语言高手 Go 语言是由 Google 出品 ...
- Go语言核心36讲(Go语言进阶技术八)--学习笔记
14 | 接口类型的合理运用 前导内容:正确使用接口的基础知识 在 Go 语言的语境中,当我们在谈论"接口"的时候,一定指的是接口类型.因为接口类型与其他数据类型不同,它是没法被实 ...
- Go语言核心36讲(Go语言进阶技术十六)--学习笔记
22 | panic函数.recover函数以及defer语句(下) 我在前一篇文章提到过这样一个说法,panic 之中可以包含一个值,用于简要解释引发此 panic 的原因. 如果一个 panic ...
- Go语言核心36讲(Go语言实战与应用一)--学习笔记
23 | 测试的基本规则和流程 (上) 在接下来的日子里,我将带你去学习在 Go 语言编程进阶的道路上,必须掌握的附加知识,比如:Go 程序测试.程序监测,以及 Go 语言标准库中各种常用代码包的正确 ...
- Go语言核心36讲(Go语言实战与应用三)--学习笔记
25 | 更多的测试手法 在本篇文章,我会继续为你讲解更多更高级的测试方法.这会涉及testing包中更多的 API.go test命令支持的,更多标记更加复杂的测试结果,以及测试覆盖度分析等等. 前 ...
- Go语言核心36讲(Go语言实战与应用四)--学习笔记
26 | sync.Mutex与sync.RWMutex 从本篇文章开始,我们将一起探讨 Go 语言自带标准库中一些比较核心的代码包.这会涉及这些代码包的标准用法.使用禁忌.背后原理以及周边的知识. ...
- Go语言核心36讲(Go语言实战与应用十四)--学习笔记
36 | unicode与字符编码 在开始今天的内容之前,我先来做一个简单的总结. Go 语言经典知识总结 在数据类型方面有: 基于底层数组的切片: 用来传递数据的通道: 作为一等类型的函数: 可实现 ...
- Go语言核心36讲(Go语言实战与应用二十二)--学习笔记
44 | 使用os包中的API (上) 我们今天要讲的是os代码包中的 API.这个代码包可以让我们拥有操控计算机操作系统的能力. 前导内容:os 包中的 API 这个代码包提供的都是平台不相关的 A ...
- Go语言核心36讲(Go语言实战与应用二十四)--学习笔记
46 | 访问网络服务 前导内容:socket 与 IPC 人们常常会使用 Go 语言去编写网络程序(当然了,这方面也是 Go 语言最为擅长的事情).说到网络编程,我们就不得不提及 socket. s ...
随机推荐
- Centos7中用Docker安装MySQL教程
第一步 安装Docker 1.1 参考这位博主给出的命令安装好 https://blog.csdn.net/weixin_43423864/article/details/109481260 第二步 ...
- Python获取时光网电影数据
Python获取时光网电影数据 一.前言 有时候觉得电影真是人类有史以来最伟大的发明,我喜欢看电影,看电影可以让我们增长见闻,学习知识.从某种角度上而言,电影凭借自身独有的魅力大大延长了人类的&quo ...
- HCNP Routing&Switching之IP安全
前文我们了解了DHCP安全相关话题,回顾请参考https://www.cnblogs.com/qiuhom-1874/p/16637627.html:今天我们来聊一聊IP安全相关话题: 技术背景 随着 ...
- KingbaseES 约束
目录 什么是约束 如何定义约束 列约束 表约束 为约束创建名称 默认约束名称 自定义约束名称 KingbaseES 的可用约束列表 CHECK约束 非空约束 UNIQUE约束 PRIMARY KEY约 ...
- tar、gzip、zip、jar是什么,怎么查看?
原创:扣钉日记(微信公众号ID:codelogs),欢迎分享,转载请保留出处. 简介 如果你是后端程序员,我想你一定见过*.tar.gz.*.zip.*.jar后缀的文件吧,这些都是压缩文件,那这些文 ...
- 【译】CLR类型加载器设计
前言 本文翻译自BotR中的一篇,原文链接 Type Loader Design ,可以帮助我们了解CLR的类型加载机制(注意是Type类型,而不是Class类),文中涉及到术语或者容易混淆的地方,我 ...
- flutter系列之:Material中的3D组件Card
目录 简介 Card详解 Card的使用 总结 简介 除了通用的组件之外,flutter还提供了两种风格的特殊组件,其中在Material风格中,有一个Card组件,可以很方便的绘制出卡片风格的界面, ...
- 使用sonarqube对java项目进行分析
目前有两种办法,第一种是使用SonarQube-Scanner-Maven,第二种是结合gitlab-ci进行 前提条件:已安装并启动sonarqube,知道访问地址和登录的用户名及密码,具体参考文档 ...
- 在 K8S 上部署以 mysql 数据库作为后端存储的单机版 nacos
Nacos 被用于: 服务发现 微服务配置信息管理 部署 nacos 时,需要用到如下两个镜像,这两个镜像均来自于 nacos 官方发布到 docker hub 的镜像, nacos/nacos-se ...
- 2_Docker
一. Docker介绍 1.1 引言 场景1: 我本地运行没问题啊 环境问题 场景2: 哪个哥们又写死循环了, 怎么这么卡 在多用户的操作系统下, 会相互影响 场景3: 淘宝在双11的时候, 用户量暴 ...