Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 7ead02b

Browse files
authored
Merge pull request #1909 from polaris1119/master
首发于 https://studygolang.com/articles/35253
2 parents e757cf9 + 8b320cc commit 7ead02b

File tree

1 file changed

+56
-70
lines changed

1 file changed

+56
-70
lines changed
Lines changed: 56 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,32 @@
1+
首发于:https://studygolang.com/articles/35253
2+
13
# 深入剖析 Golang Pprof 标签
24

35
Polar Signals 提供的持续分析工具可以和任何格式的 pprof 分析适配,Go 深度集成了 [pprof](https://github.com/google/pprof) 甚至支持了它的`标签`特性。
4-
然而,自从我们发布了我们的 [持续分析产品](https://www.polarsignals.com/blog/posts/2021/02/09/announcing-polar-signals-continuous-profiler/) 之后,
5-
收到了很多工程师的反馈,发现许多工程师不知道如何去分析, 或者不知道分析能给他们带来什么好处。这篇文章主要剖析 pprof 标签,并会结合一些 Go 的示例代码去分析。
6+
7+
然而,自从我们发布了我们的 [持续分析产品](https://www.polarsignals.com/blog/posts/2021/02/09/announcing-polar-signals-continuous-profiler/) 之后,收到了很多工程师的反馈,发现许多工程师不知道如何去分析, 或者不知道分析能给他们带来什么好处。这篇文章主要剖析 pprof 标签,并会结合一些 Go 的示例代码去分析。
68

79
## 基础
810

9-
pprof 标签只支持 Go 的 CPU 分析器。Go 的分析器是抽样分析,这意味着它只会根据特定的频率(默认是 1 秒钟 100 次)去获取执行中函数的调用栈并记录。
10-
简单来说,开发者如果使用标签,在分析器取样时就可以将函数的调用栈进行区分,然后只聚合i具有相同标签的函数调用栈。
11+
pprof 标签只支持 Go 的 CPU 分析器。Go 的分析器是抽样分析,这意味着它只会根据特定的频率(默认是 1 秒钟 100 次)去获取执行中函数的调用栈并记录。简单来说,开发者如果使用标签,在分析器取样时就可以将函数的调用栈进行区分,然后只聚合具有相同标签的函数调用栈。
1112

1213
Go 在 `runtime/pprof` 包中已经支持了标签检测,可以使用 [`pprof.Do`](https://golang.org/pkg/runtime/pprof/#Do) 函数非常方便的使用。
1314

1415
```golang
1516
pprof.Do(ctx, pprof.Labels("label-key", "label-value"), func (ctx context.Context) {
16-
// execute labeled code
17+
// execute labeled code
1718
})
1819
```
1920

2021
## 进阶
2122

22-
为了进行演示如何使用 pprof
23-
标签,我们创建了一个包含许多示例的仓库,这个示例仓库代码作为这篇文章的内容指导。
24-
仓库地址:[https://github.com/polarsignals/pprof-labels-example](https://github.com/polarsignals/pprof-labels-example)
23+
为了进行演示如何使用 pprof 标签,我们创建了一个包含许多示例的仓库,这个示例仓库代码作为这篇文章的内容指导。仓库地址:[https://github.com/polarsignals/pprof-labels-example](https://github.com/polarsignals/pprof-labels-example)
2524

26-
示例代码的 [`main.go`](https://github.com/polarsignals/pprof-labels-example/blob/60accf8b4fbebcd5f96b3743663af5745ef74596/main.go)
27-
通过将 `tanant` 传递给 `iterate` 函数实现了大量的 for 循环, 其中`tanant1` 做了 10 亿次循环,而 `tanant2` 做了 1 亿次循环,
28-
同时会记录 CPU 的分析日志并将其写入 `./cpuprofile.pb.gz`
25+
示例代码的 [`main.go`](https://github.com/polarsignals/pprof-labels-example/blob/60accf8b4fbebcd5f96b3743663af5745ef74596/main.go) 通过将 `tanant` 传递给 `iterate` 函数实现了大量的 for 循环, 其中`tanant1` 做了 10 亿次循环,而 `tanant2` 做了 1 亿次循环,同时会记录 CPU 的分析日志并将其写入 `./cpuprofile.pb.gz`
2926

30-
为了演示如何在 pprof 的分析日志中展示 pprof 标签,
31-
[`printprofile.go`](https://github.com/polarsignals/pprof-labels-example/blob/60accf8b4fbebcd5f96b3743663af5745ef74596/printprofile.go)
32-
来打印每次抽样函数调用栈以及样本值,还有收集到样本的标签。
27+
为了演示如何在 pprof 的分析日志中展示 pprof 标签,用 [`printprofile.go`](https://github.com/polarsignals/pprof-labels-example/blob/60accf8b4fbebcd5f96b3743663af5745ef74596/printprofile.go) 来打印每次抽样函数调用栈以及样本值,还有收集到样本的标签。
3328

34-
如果我们注释掉 [`pprof.Do` 的这部分](https://github.com/polarsignals/pprof-labels-example/blob/60accf8b4fbebcd5f96b3743663af5745ef74596/main.go#L39-L41)
35-
我们将无法进行标签检测,运行 [`printprofile.go`](https://github.com/polarsignals/pprof-labels-example/blob/60accf8b4fbebcd5f96b3743663af5745ef74596/printprofile.go)
36-
代码,让我们看看没有标签的抽样分析结果:
29+
如果我们注释掉 [`pprof.Do` 的这部分](https://github.com/polarsignals/pprof-labels-example/blob/60accf8b4fbebcd5f96b3743663af5745ef74596/main.go#L39-L41) ,我们将无法进行标签检测,运行 [`printprofile.go`](https://github.com/polarsignals/pprof-labels-example/blob/60accf8b4fbebcd5f96b3743663af5745ef74596/printprofile.go) 代码,让我们看看没有标签的抽样分析结果:
3730

3831
```bash
3932
runtime.main
@@ -94,88 +87,81 @@ map[tenant:[tenant2]]
9487
Total: 2.82s
9588
```
9689
97-
将所有抽样加起来总共花费了 2.82 秒,然而,因为调用 `iterate` 时,我们添加了标签,所以我们能在结果中区分哪个 `tenant` 导致了更多的 CPU 占用 。
98-
现在我们可以看到 `tenant1` 花费了总时间 2.82 秒中的 2.55 秒(2540000000ns + 10000000ns = 2550000000ns = 2.55s)。
90+
将所有抽样加起来总共花费了 2.82 秒,然而,因为调用 `iterate` 时,我们添加了标签,所以我们能在结果中区分哪个 `tenant` 导致了更多的 CPU 占用。现在我们可以看到 `tenant1` 花费了总时间 2.82 秒中的 2.55 秒(2540000000ns + 10000000ns = 2550000000ns = 2.55s)。
9991
10092
让我们看看抽样的原始日志(还有它们的元数据),去更深入理解一下它们的格式:
10193
10294
```bash
10395
$ protoc --decode perftools.profiles.Profile --proto_path ~/src/github.com/google/pprof/proto profile.proto < cpuprofile.pb | grep -A12 "sample {"
10496
sample {
105-
location_id: 1
106-
location_id: 2
107-
location_id: 3
108-
location_id: 4
109-
location_id: 5
110-
value: 1
111-
value: 10000000
97+
location_id: 1
98+
location_id: 2
99+
location_id: 3
100+
location_id: 4
101+
location_id: 5
102+
value: 1
103+
value: 10000000
112104
}
113105
sample {
114-
location_id: 1
115-
location_id: 2
116-
location_id: 3
117-
location_id: 4
118-
location_id: 5
119-
value: 254
120-
value: 2540000000
121-
label {
122-
key: 14
123-
str: 15
124-
}
106+
location_id: 1
107+
location_id: 2
108+
location_id: 3
109+
location_id: 4
110+
location_id: 5
111+
value: 254
112+
value: 2540000000
113+
label {
114+
key: 14
115+
str: 15
116+
}
125117
}
126118
sample {
127-
location_id: 6
128-
location_id: 2
129-
location_id: 3
130-
location_id: 4
131-
location_id: 5
132-
value: 1
133-
value: 10000000
134-
label {
135-
key: 14
136-
str: 15
137-
}
119+
location_id: 6
120+
location_id: 2
121+
location_id: 3
122+
location_id: 4
123+
location_id: 5
124+
value: 1
125+
value: 10000000
126+
label {
127+
key: 14
128+
str: 15
129+
}
138130
}
139131
sample {
140-
location_id: 1
141-
location_id: 2
142-
location_id: 3
143-
location_id: 7
144-
location_id: 5
145-
value: 26
146-
value: 260000000
147-
label {
148-
key: 14
149-
str: 16
150-
}
132+
location_id: 1
133+
location_id: 2
134+
location_id: 3
135+
location_id: 7
136+
location_id: 5
137+
value: 26
138+
value: 260000000
139+
label {
140+
key: 14
141+
str: 16
142+
}
151143
}
152144
```
153145
154-
我们可以看到每个抽样都由许多 ID 组成,这些 ID 指向它们在分析日志 `location` 数组中的位置,除了这些 ID 还有几个值 。
155-
仔细看下 `printprofile.go` 程序,你会发现它使用了每个抽样的最后一个抽样 value。
156-
实际上,Go 的 CPU 分析器会记录两个 value,第一个代表这个调用栈在一次分析区间被记录样本的数量,第二个代表它花费了多少纳秒。
157-
pprof 的定义描述当没设置 `default_sample_type` 时(在 Go 的 CPU 配置中设置),就使用所有 value 中的最后一个,
158-
因此我们使用的是代表纳秒的 value 而不是样本数的 value。最后,我们可以打印出标签,它是 pprof 定义的一个以字符串组成的字典。
146+
我们可以看到每个抽样都由许多 ID 组成,这些 ID 指向它们在分析日志 `location` 数组中的位置,除了这些 ID 还有几个值。仔细看下 `printprofile.go` 程序,你会发现它使用了每个抽样的最后一个抽样 value。实际上,Go 的 CPU 分析器会记录两个 value,第一个代表这个调用栈在一次分析区间被记录样本的数量,第二个代表它花费了多少纳秒。pprof 的定义描述当没设置 `default_sample_type` 时(在 Go 的 CPU 配置中设置),就使用所有 value 中的最后一个,因此我们使用的是代表纳秒的 value 而不是样本数的 value。最后,我们可以打印出标签,它是 pprof 定义的一个以字符串组成的字典。
159147
160148
最后,因为用标签去区分数据,我们可以让可视化界面更直观。
161149
162150
![](https://raw.githubusercontent.com/studygolang/gctt-images2/master/20210413-Demystifying-Pprof-Labels-with-Go/pprof-callgraph-with-labels.png)
163151
164-
你可以在 Polar Signals 网站去更详细的了解上面的这次分析:[https://share.polarsignals.com/2063c5c/](https://share.polarsignals.com/2063c5c/)
152+
你可以在 Polar Signals 网站去更详细的了解上面的这次分析:[https://share.polarsignals.com/2063c5c/](https://share.polarsignals.com/2063c5c/)
165153
166154
## 结论
167155
168-
pprof 标签是帮助我们理解程序不同执行路径非常有用的方法,许多人喜欢在多租户系统中使用它们,目的就是为了能够定位在他们系统中出现的由某一个租户导致的性能问题。
169-
就像开头说的,只需要调用 [`pprof.Do`](https://golang.org/pkg/runtime/pprof/#Do) 就可以了。
156+
pprof 标签是帮助我们理解程序不同执行路径非常有用的方法,许多人喜欢在多租户系统中使用它们,目的就是为了能够定位在他们系统中出现的由某一个租户导致的性能问题。就像开头说的,只需要调用 [`pprof.Do`](https://golang.org/pkg/runtime/pprof/#Do) 就可以了。
170157
171-
Polar Signals 提供的持续分析工具也支持了 pprof 标签的可视化界面和报告,
172-
如果你想参与个人体验版请点击:[申请资格](https://www.polarsignals.com/#request-access)
158+
Polar Signals 提供的持续分析工具也支持了 pprof 标签的可视化界面和报告,如果你想参与个人体验版请点击:[申请资格](https://www.polarsignals.com/#request-access)。
173159
174160
---
175161
via: https://www.polarsignals.com/blog/posts/2021/04/13/demystifying-pprof-labels-with-go/
176162
177163
作者:[Frederic Branczyk](https://twitter.com/fredbrancz)
178164
译者:[h1z3y3](https://h1z3y3.me)
179-
校对:[校对者ID](https://github.com/校对者ID)
165+
校对:[polaris1119](https://github.com/polaris1119)
180166
181167
本文由 [GCTT](https://github.com/studygolang/GCTT) 原创编译,[Go 中文网](https://studygolang.com/) 荣誉推出

0 commit comments

Comments
 (0)