当前位置: 首页 > news >正文

go资深之路笔记(八) 基准测试

一、用法

aa_test.go

func BenchmarkAdd(b *testing.B) {a := "shdhAAsaBBjhsd"for i := 0; i < b.N; i++ {res := strings.Replace(a, "AA", "BB", -1)_ = res}
}

然后执行测试:

go test --bench=BenchmarkAdd --benchmem aa_test.go

输出:
![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/3f78d8a657a04457a68e509670ef5c1d.png

细节分析:

  1. 脚本名必须是 xxx_test.go
  2. 函数名必须是 Benchmark开头
  3. 函数参数必须是 *testing.B
  4. _ = res 是为了防止go编译器进行优化(当然去掉编译也不会通过)
  5. –bench 是指定函数,他的值是正则表达式。 --bench=. 是匹配所有基准函数
  6. –benchmem 是分析内存分配
  7. 参数前的两个横杠,也可以用一个。 --bench等于-bench【不得不说go的设计真的很人性】
  8. 图片红框第一个 执行次数,剩下三个是每次执行时消耗时间,分配内存字节,分配内存次数

二、参数大全

# 启用内存分配统计
go test -bench=. -benchmem### 性能分析 ##################
# 指定测试运行时间
go test -bench=. -benchtime=5s      # 运行5秒
go test -bench=. -benchtime=100x    # 运行100次迭代	b.N = 100# 一键执行多次
go test -bench=. -count=10# 并行运行测试(cpu多不一定变快,除非有多goroutine任务)
## 这里要说一下有意思的点,开一个goroutine的 2-4KB 栈内存以及一定时间,如果对应耗时比较少的任务,
##其实根本没必要开 goroutine,因为你开goroutine有可能耗时更多,内存更多是绝对的,所以goroutine不是万金油。
go test -bench=. -cpu=4### cpu 内存分析 借助 pprof ################## 
# 生成 CPU profile
go test -bench=. -cpuprofile=cpu.pprof# 生成内存 profile  
go test -bench=. -benchmem -memprofile=mem.pprof# 生成阻塞 profile
go test -bench=. -blockprofile=block.pprof# 生成互斥锁 profile
go test -bench=. -mutexprofile=mutex.pprof#### 并发和调度相关 #################
# 指定不同的 GOMAXPROCS 值
go test -bench=. -cpu=1,2,4,8# 设置并行度(与 -cpu 不同)
go test -bench=. -parallel=8# 跟踪调度信息(调试用)
go test -bench=. -trace=trace.out

2.1 b.RunParallel

看了上面的参数,你大概知道想要实现并发效果,并不是加go就行的,这里可以用
b.RunParallel(这个函数的内部实现大概是启用goroutine的数量等同于GOMAXPROCS(go可用核心数),或者GOMAXPROCS的倍数;从而实现cpu核心的充分使用。)


func cal(a string) {res := strings.Replace(a, "AA", "BB", -1)_ = res
}// var res = ""
var chan1 = make(chan struct{}, 8)func BenchmarkAdd(b *testing.B) {a := "shdhAAsaBBjhsd"b.RunParallel(func(pb *testing.PB) {for pb.Next() {cal(a)}})
}

执行:

go test -bench=BenchmarkAdd -benchmem -cpu=1,2,4,8,16,24 aa_test.go

效果:
在这里插入图片描述

2.2 有缓存的channel

有个很有意思的点:现实环境下,不可能让goroutine一直上涨,特别是cpu密集型任务,不限制的话会内存和cpu会爆炸性增长!!(for go func() 就会如此, b.RunParallel 不会是因为限制了goroutin数量)但是一般普通的限制方式,比如 用有缓存的channel来限制,你会发现cpu不是越大越好:


func cal(a string) {defer func() {<-chan1}()res := strings.Replace(a, "AA", "BB", -1)_ = res
}var chan1 = make(chan struct{}, 8)func BenchmarkAdd(b *testing.B) {a := "shdhAAsaBBjhsd"//for i := 0; i < b.N; i++ {	//	go cal(a)//}b.SetParallelism(10)b.RunParallel(func(pb *testing.PB) {for pb.Next() {chan1 <- struct{}{}		// 用channel来控制同时并发goroutine数量cal(a)}})
}

执行你会发现cpu越大你会:
在这里插入图片描述

原因是: 有缓存的channel 内部构造是 有锁的,也就是说 存在多个cpu争抢同一个内存的情况,这个时候可能会涉及缓存一致性,以及获取锁过程中阻塞或者自旋而产生的消耗。
这就产生了两个问题:

  1. channel限流的操作并不一定适合,可以优化;【这个之后的文章会再写,篇幅不够】
  2. 并不是cpu数量越大约好的。(这个其实再上一个例子的BenchmarkAdd-24 也能看出来)

2.3 总结:

这是探索命令过程中的一些笔记,可能有点乱,稍微总结下

  1. b.RunParallel 函数可以用来测试代码在那个核心数下运行最佳(也可以测是否panic)
  2. 用有缓存channel 来限流会导致争抢,cpu核心数越高越明显(可优化)
  3. 基准测试的时候其实是没有计算goroutine创建的消耗和内存的,实际上一个goroutine创建占用内存 是2kB~4kB, 所以除了io密集型或者cpu密集型任务,有时候可能用同步的方式执行会更快

2.4 和 pprof联合工作

就是上面的命令

# 生成 CPU profile
go test -bench=. -cpuprofile=cpu.pprof# 生成内存 profile  
go test -bench=. -benchmem -memprofile=mem.pprof# 生成阻塞 profile
go test -bench=. -blockprofile=block.pprof# 生成互斥锁 profile
go test -bench=. -mutexprofile=mutex.pprof

需要先安装 graphviz(火焰图生成工具)

sudo apt-get install graphviz

直接用 pprof工具打开再网页展示

 go tool pprof -http=localhost:8080  cpu.out

2.5 和 trace联合工作

生成 trace测试文件

go test -bench=. -benchtime=10000x -trace=trace.out

用trace工具打开文件

go tool trace trace.out

ps:-benchtime=10000x 代表执行10000次,因为不指定的话一个测试动则几十上百万次,trace根本打不开

三、坑点:

1.循环开始前有耗时也会被计入,我们可以用 b.ResetTimer() 重置计时器,排除上方初始化时间
或者用 b.StopTimer() 和 b.StartTimer() 开始计时

  1. 如果在 b.Loop() 或 b.N 循环中使用了 break 等语句提前退出,会导致实际迭代次数远少于预期,测试结果严重失准,且框架可能不会告警。

  2. 善用 benchtime 和count: 测量非常快(纳秒级)的操作时,单次测试结果极易受机器负载、电源管理等环境因素干扰,导致结果不稳定。
    使用 -benchtime 延长单次测试时间,例如 -benchtime=5s。
    使用 -count 多次运行测试,并结合 benchstat 工具进行统计分析
    benchstat 用法:

go test -bench=BenchmarkAdd -benchmem -count=5 aa_test.go | tee stats.txt
benchstat stats.txt
http://www.dtcms.com/a/491599.html

相关文章:

  • 第1讲:Go调度器GMP模型深度解析
  • C++ 关键字 static 面试高频问题汇总
  • 网站建设jnlongji百度技术培训中心
  • m版网站开发怎样创建网页
  • 基于自适应差分进化算法的MATLAB实现
  • 男人女人做那事网站如何创建一个互联网平台
  • RocketMQ 与 Kafka 架构与实现详解对比
  • 设计模式篇之 观察者模式 Observer
  • Tripo 3D AI 功能与技术解析
  • 千库网素材搜索引擎优化培训班
  • 能打开各种网站的浏览器appwordpress文章表情
  • docker学习 (3)网络与防火墙
  • 智元发布新一代工业级交互式具身作业机器人精灵G2,多场景“六边形战士” 首发前已获数亿元订单
  • 如何在线烧录梦丘MOS表情机器人固件
  • 河北省建设网站锁安装什么驱动网站制作效果好
  • 链式法则在神经网络中的应用:原理与实现详解
  • 前段模板网站南京网站开发南京乐识正规
  • K8s 核心架构是什么?组件怎么协同工作的?
  • C语言---函数
  • 做网站的费用入什么科目哈尔滨网站建设外包公司
  • YOLOv4深入解析:从原理到实践的全方位指南
  • MATLAB机器学习入门教程
  • 网站建设的好处论文网络营销以什么为中心
  • android studio设置大内存,提升编译速度
  • 从原理到实战:数据库索引、切片与四表联查全解析
  • 重庆建站免费模板mui做wap网站
  • 思想实验:如何使用MeshGPT?
  • Vue3 + TypeScript 实现 CAN 报文实时回放与合并显示
  • seo网站推广有哪些网站维护与优化教程
  • LeetCode 2598. 执行操作后的最大 MEX