本文侧重于速记和实际操作部分的内容,其他方面可回顾资料
ep5代码规范
这里是看课后的记忆之谈,只是记下了自己认为比较重要且常用的部分,并不全面
format缩写为fmt
注释要说明前后联系
函数命名内不用带包的名字,因为调用 . 说明了包
return 写return即可不用加else
ep6.1克隆 github.com/wolfogre/go… 到本地,保证能够编译运行
克隆到本地
打开Git Bash
git clone https://github.com/wolfogre/go-pprof-practice.git
请注意将 "(github.com/wolfogre/go…)" 替换为实际要克隆的仓库的 URL,并加git 克隆完成后(在文件管理器中可以搜索到了) 克隆到用户目录下的写法
cd go-pprof-practice
若将项目克隆到桌面上(Windows 系统的路径示例)
cd C:\Users\YourUsername\Desktop
git clone https://github.com/wolfogre/go-pprof-practice.git
编译和运行
法一
命令行终端中输入
进入克隆下来的项目目录
cd go-pprof-practice
编译
go build
运行
./go-pprof-practice
法二
对于使用 Make 的项目 当打开git输入以下语句
make -v
G@GJul MINGW64 ~/go-pprof-practice (master)
$ make
bash: make: command not found
这意味着操作系统(在这里是 Windows,MINGW64 是一个类似于 Linux 的终端模拟器)中没有找到 make 命令。
【win11上安装MinGW最新版gcc12】www.bilibili.com/video/BV1Ld…
bash: make: command not found
MinGW 只提供了名字为 mingw32-make.exe 的执行文件,该文件和 make.exe 功能一样,为了make执行时能找到该文件,复制mingw32-make.exe一份,并将复制文件命名为make.exe
此时
make
与法一中的 go bulid作用相同
ep6.2尝试使用 test 命令,编写并运行简单测试
Add a test - The Go Programming Language
新建一名为greetings的project
greetings目录下创建一个greetings_test.go的文件,一个greetings.go文件
以下是 greetings.go 文件的示例内容:
package greetings
import (
"errors"
"fmt"
)
// Hello 函数为给定的名字返回问候消息。
func Hello(name string) (string, error) {
// 如果没有给定名字,返回一个错误。
if name == "" {
return "", errors.New("空名字")
}
message := fmt.Sprintf("你好,%s!", name)
return message, nil
}
在greetings_test.go文件中,添加以下代码:
package greetings
import (
"testing"
"regexp"
)
// TestHelloName 调用 greetings.Hello 函数,并检查返回值是否有效。
func TestHelloName(t *testing.T) {
name := "Gladys"
want := regexp.MustCompile(`\b`+name+`\b`)
msg, err := Hello("Gladys")
if !want.MatchString(msg) || err != nil {
t.Fatalf(`Hello("Gladys") = %q, %v, want match for %#q, nil`, msg, err, want)
}
}
// TestHelloEmpty 调用 greetings.Hello 函数,并检查是否返回错误。
func TestHelloEmpty(t *testing.T) {
msg, err := Hello("")
if msg != "" || err == nil {
t.Fatalf(`Hello("") = %q, %v, want "", error`, msg, err)
}
}
此时还缺少go.mod文件
- 打开终端或命令提示符。
- 使用
cd命令导航到项目目录C:\GoLandProjects\greetings
cd C:\GoLandProjectsfile\greetings
运行以下命令来初始化一个新的Go模块:
go mod init greetings
在greetings目录下的命令行中运行go test命令来执行测试。go test命令会执行以Test开头的测试函数,以及在文件名以_test.go结尾的测试文件。
PASS
ok greetings 1.295s
3. 希望得到更详细的输出,可以添加-v标志来获取所有测试及其结果的列表。
go test -v
=== RUN TestHelloName --- PASS: TestHelloName (0.00s) === RUN TestHelloEmpty --- PASS: TestHelloEmpty (0.00s) PASS ok greetings 0.621s
ep6.3尝试使用 -bench 参数,对编写的函数进行性能测试(续ep6.2)
阅读资料testing package - testing - Go Packages
在测试文件greetings_test.go中添加一个性能测试函数。示例代码如下:
// BenchmarkHello benchmarks the performance of the Hello function.
func BenchmarkHello(b *testing.B) {
// Run the Hello function b.N times.
for i := 0; i < b.N; i++ {
Hello("Gladys")
}
}
在上面的示例中,我们编写了一个性能测试函数BenchmarkHello。该函数使用testing.B类型的参数,这是用于性能测试的辅助对象。在测试函数中,我们运行Hello函数b.N次,其中b.N是由测试框架决定的运行次数,以便在足够长的时间内得到可靠的测试结果。
现在,在项目目录中运行性能测试命令:
go test -bench=.
运行以上命令时,Go测试框架会执行BenchmarkHello函数并测量其性能。
请注意,性能测试的结果可能会因为不同的硬件配置、运行环境和代码优化而有所不同。因此,在进行性能测试时,最好在相同的硬件环境下多次运行测试,并观察结果的趋势。
可以选择的 -bench 参数语法如下:
-bench=.:运行所有的基准测试函数。
ok greetings 1.952s
-bench=Pattern.:运行所有与正则表达式 Pattern 匹配的基准测试函数。
BenchmarkHello-20 16258156 74.45 ns/op PASS ok greetings 1.952s
ep6.4性能优化指南
-
使用性能分析:
- 利用内置的性能分析工具如
pprof来识别代码中的性能瓶颈。 - 运行
go tool pprof来分析CPU和内存的使用情况。
- 利用内置的性能分析工具如
-
进行基准测试:
- 使用
testing包编写基准测试来测量代码中关键部分的性能。 - 使用
go test -bench命令来运行基准测试。
- 使用
-
减少垃圾回收(GC)压力:
- 减少不必要的对象创建,从而降低垃圾回收的频率和持续时间。
- 使用对象池和
sync.Pool来复用对象。
-
谨慎分配内存:
- 避免在热代码段中进行不必要的内存分配。
- 在可能的情况下,优先使用栈分配而不是堆分配。
- 对于已知大小的数组或切片,进行预分配。
-
使用内置的并发特性:
- 利用 goroutines 和 channels 来实现并发编程。
- 在访问共享数据时要注意数据竞争,并使用互斥锁或原子操作来进行同步。
-
利用
sync/atomic包:- 处理共享变量并避免竞态条件时,使用
sync/atomic包来替代锁。 - 原子操作在性能上更高效,但在使用时要注意一些限制。
- 处理共享变量并避免竞态条件时,使用
-
减少内存拷贝:
- 尽量减少不必要的数据拷贝,可以使用指针传递或者使用切片引用原始数据。
- 使用
io.Reader和io.Writer接口进行高效的数据传输。
-
优化缓存局部性:
- 组织数据结构以最大化缓存局部性,从而减少缓存未命中的次数。
- 在处理 goroutines 之间的共享数据时要注意虚假共享。
-
选择合适的数据结构:
- 根据具体的使用场景选择合适的数据结构。例如,使用 map 来进行快速的键值访问,使用切片来处理变长序列,使用数组来处理固定长度集合。
-
编译器优化标志:
- 使用编译器的优化标志如
-O或者-gcflags来指示编译器进行代码优化。 - 注意过于激进的优化可能会导致较长的编译时间。
- 避免过度的字符串拼接:
- 字符串拼接可能会创建多个中间字符串,增加内存使用和垃圾回收的压力。
- 考虑使用
strings.Builder或者bytes.Buffer来进行高效的字符串拼接。
- 使用合适的算法:
- 选择适合具体任务的算法和数据结构,它们会对性能产生重要影响。
需要注意的是,过早进行优化往往事倍功半,因此在做出优化之前,应该先进行性能分析,重点放在编写清晰易读的代码上,然后根据需要识别和解决性能瓶颈。
ep7.1pprof(续ep6.1)
在 main.go 中加入 import _ "net/http/pprof" 后,运行程序时就会自动启动 PProf 的 HTTP 服务器,它会监听一个默认的端口(一般是 :6060),以便在浏览器中访问 http://localhost:6060/debug/pprof/ 来查看性能分析的数据。
import _ "net/http/pprof"
在terminal打开一个local
go build
./go-pprof-practice
接着在浏览器中访问 http://localhost:6060/debug/pprof/,将看到 PProf 的 Web UI。这里可以查看各种性能数据,如 CPU 和内存的使用情况,以及进行各种性能分析。
/debug/pprof/
Set debug=1 as a query parameter to export in legacy text format
Types of profiles available:
| Count | Profile |
|---|---|
| 13 | allocs |
| 2 | block |
| 0 | cmdline |
| 47 | goroutine |
| 13 | heap |
| 1 | mutex |
| 0 | profile |
| 7 | threadcreate |
| 0 | trace |
Profile Descriptions:
-
allocs:
A sampling of all past memory allocations
-
block:
Stack traces that led to blocking on synchronization primitives
-
cmdline:
The command line invocation of the current program
-
goroutine:
Stack traces of all current goroutines. Use debug=2 as a query parameter to export in the same format as an unrecovered panic.
-
heap:
A sampling of memory allocations of live objects. You can specify the gc GET parameter to run GC before taking the heap sample.
-
mutex:
Stack traces of holders of contended mutexes
-
profile:
CPU profile. You can specify the duration in the seconds GET parameter. After you get the profile file, use the go tool pprof command to investigate the profile.
-
threadcreate:
Stack traces that led to the creation of new OS threads
-
trace:
A trace of execution of the current program. You can specify the duration in the seconds GET parameter. After you get the trace file, use the go tool trace command to investigate the trace.
需要在terminal中打开两个local 在保证一个local程序是运行的情况下(ps:ctrl+c停止运行)在另一个local中输入
go tool pprof -seconds=10 http://localhost:6060/debug/pprof/profile
在十秒钟内收集性能数据,在命令行中呈现。
Type: cpu
Time: Aug 2, 2023 at 11:43am (CST)
Duration: 10.12s, Total samples = 2.35s (23.22%)
(pprof) 输入top
Showing nodes accounting for 2.30s, 97.87% of 2.35s total
Dropped 39 nodes (cum <= 0.01s)
Showing top 10 nodes out of 11
flat flat% sum% cum cum%
2.25s 95.74% 95.74% 2.28s 97.02% github.com/wolfogre/go-pprof-
practice/animal/felidae/tiger.(*Tiger).Eat
0.03s 1.28% 97.02% 0.03s 1.28% runtime.asyncPreempt
0.01s 0.43% 97.45% 0.02s 0.85% fmt.(*pp).doPrintln
0.01s 0.43% 97.87% 0.02s 0.85% runtime.blockevent
0 0% 97.87% 0.03s 1.28% fmt.Sprintln
0 0% 97.87% 0.03s 1.28% github.com/wolfogre/go-pprof-
practice/animal/felidae/cat.(*Cat).Live
0 0% 97.87% 2.28s 97.02% github.com/wolfogre/go-pprof-
practice/animal/felidae/tiger.(*Tiger).Live
0 0% 97.87% 0.03s 1.28% log.Println
0 0% 97.87% 2.32s 98.72% main.main
0 0% 97.87% 2.32s 98.72% runtime.main
在 go tool pprof 输出中,以下是 flat、flat%、sum%、cum 和 cum% 的含义:
-
flat: 表示函数在执行过程中所占用的总时间。它指的是函数本身的执行时间,并不包括它调用其他函数的时间。单位通常是纳秒(ns)或微秒(μs)。 -
flat%: 表示函数在执行过程中所占用的总时间在整个执行时间中的百分比。这个百分比是相对于整个程序的执行时间来计算的。例如,如果某个函数的flat%是 10%,则表示该函数的执行时间占整个程序执行时间的 10%。 -
sum%: 表示当前函数以及它调用的所有子函数的总时间在整个执行时间中的百分比。这包括了函数本身执行的时间以及它调用的所有子函数的执行时间。对于递归函数,可能会导致sum%大于 100%。 -
cum: 表示从程序开始执行到当前函数执行完成的时间。它包括当前函数的执行时间以及它之前所有函数的执行时间,因此是一个累积时间。 -
cum%: 表示从程序开始执行到当前函数执行完成的时间在整个执行时间中的百分比。这个百分比是相对于整个程序的执行时间来计算的。例如,如果某个函数的cum%是 20%,则表示从程序开始执行到该函数执行完成的时间占整个程序执行时间的 20%。