benchmark 基准测试

可以度量某个函数或方法的性能,也就是说,
如果我们知道性能的瓶颈点在哪里,benchmark 是一个非常好的方式
。但是面对一个未知的程序,如何去分析这个程序的性能,并找到瓶颈点
pprof 就是用来解决这个问题的。pprof 包含两部分:

编译到程序中的 runtime/pprof 包
性能剖析工具 go tool pprof

启动 CPU 分析时,运行时(runtime) 将每隔 10ms 中断一次,
记录此时正在运行的协程(goroutines) 的堆栈信息。程序运行结
束后,可以分析记录的数据找到最热代码路径(hottest code paths)

  1. CPU性能分析
    一个函数在性能分析数据中出现的次数越多,说明执行该函数的代码
    路径(code path)花费的时间占总运行时间的比重越大。

  2. 内存性能分析
    内存性能分析(Memory profiling) 记录堆内存分配时的堆栈信息
    ,忽略栈内存分配信息。

内存性能分析启用时,默认每1000次采样1次,这个比例是可以调整
的。因为内存性能分析是基于采样的,因此基于内存分析数据来判断
程序所有的内存使用情况是很困难的

  1. 阻塞性能分析
    阻塞性能分析(block profiling) 是 Go 特有的。

阻塞性能分析用来记录一个协程等待一个共享资源花费的时间。在判断程序的并发瓶颈时会很有用。阻塞的场景包括:

在没有缓冲区的信道上发送或接收数据。
从空的信道上接收数据,或发送数据到满的信道上。
尝试获得一个已经被其他协程锁住的排它锁。
一般情况下,当所有的 CPU 和内存瓶颈解决后,才会考虑这一类分析

  1. 锁性能分析
    锁性能分析(mutex profiling) 与阻塞分析类似,但专注于因为
    锁竞争导致的等待或延时。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
package main

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

func generate(n int) []int {
rand.Seed(time.Now().UnixNano())
nums := make([]int, 0)
for i := 0; i < n; i++ {
nums = append(nums, rand.Int())
}
return nums
}
func bubbleSort(nums []int) {
for i := 0; i < len(nums); i++ {
for j := 1; j < len(nums)-i; j++ {
if nums[j] < nums[j-1] {
nums[j], nums[j-1] = nums[j-1], nums[j]
}
}
}
}

func main() {
//测试结果写入文件中
f, _ := os.OpenFile("cpu.pprof", os.O_CREATE|os.O_RDWR, 0644)
defer f.Close()
//测试这个应用程序的CPU性能
pprof.StartCPUProfile(f)
defer pprof.StopCPUProfile()
//以上几步可以用 库 "github.com/pkg/profile"
//defer profile.Start().Stop()一行代替
n := 10
for i := 0; i < 5; i++ {
nums := generate(n)
bubbleSort(nums)
n *= 10
}
}

package main

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

func generate(n int) []int {
rand.Seed(time.Now().UnixNano())
nums := make([]int, 0)
for i := 0; i < n; i++ {
nums = append(nums, rand.Int())
}
return nums
}
func bubbleSort(nums []int) {
for i := 0; i < len(nums); i++ {
for j := 1; j < len(nums)-i; j++ {
if nums[j] < nums[j-1] {
nums[j], nums[j-1] = nums[j-1], nums[j]
}
}
}
}

func main() {
//测试结果写入文件中
f, _ := os.OpenFile("cpu.pprof", os.O_CREATE|os.O_RDWR, 0644)
defer f.Close()
//测试这个应用程序的CPU性能
pprof.StartCPUProfile(f)
defer pprof.StopCPUProfile()
//以上几步可以用 库 "github.com/pkg/profile"
//defer profile.Start().Stop()一行代替
n := 10
for i := 0; i < 5; i++ {
nums := generate(n)
bubbleSort(nums)
n *= 10
}
}


package main

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

func generate(n int) []int {
rand.Seed(time.Now().UnixNano())
nums := make([]int, 0)
for i := 0; i < n; i++ {
nums = append(nums, rand.Int())
}
return nums
}
func bubbleSort(nums []int) {
for i := 0; i < len(nums); i++ {
for j := 1; j < len(nums)-i; j++ {
if nums[j] < nums[j-1] {
nums[j], nums[j-1] = nums[j-1], nums[j]
}
}
}
}

func main() {
//测试结果写入文件中
f, _ := os.OpenFile("cpu.pprof", os.O_CREATE|os.O_RDWR, 0644)
defer f.Close()
//测试这个应用程序的CPU性能
pprof.StartCPUProfile(f)
defer pprof.StopCPUProfile()
//以上几步可以用 库 "github.com/pkg/profile"
//defer profile.Start().Stop()一行代替
n := 10
for i := 0; i < 5; i++ {
nums := generate(n)
bubbleSort(nums)
n *= 10
}
}

然后执行命令

1
2
#分析该文件 打开lochost:9999即可
go tool pprof -http=:9999 cpu.pprof

也可以命令行查看

1
2
3
4
go tool pprof cpu.pprof
top 查看各函数的CPU占用比例
top --cum 按照累计消耗排序
help 帮助