Go语言-测试与性能分析



在接触的几门计算机编程语言中,都会有与之对应的单元测试与性能分析方法或者工具。Go语言当然也不例外,本文将记录Go语言中测试与性能分析的内容,其目的是为了掌握Go语言中测试和性能分析和提出一些容易出现的问题。

目录

  • Go语言-测试与性能分析
    • 1. 测试
      • 1.1 单元测试
        • 1.1.1 第一个单元测试
      • 1.2 表组测试
      • 1.3 test main
      • 1.3 覆盖测试
        • 小结
    • 2. 基准测试
      • 2.1 分析代码

1. 测试

Go语言里有几种方法可用于单元测试:

  • 基础测试 - 一组参数和结果测试代码
  • 表组测试 - 多组参数和结果测试代码

1.1 单元测试

Go语言中要想进行单元测试,就必须使用testing 包,它提供自动化测试工具。通过go test命令能够自动执行以下形式的函数:

func TestXXX(*testing.T){...
}
1.1.1 第一个单元测试

在使用单元测试之前,先写一个用于被测试的方法,这里写一个打乱slice得函数。

func RandSlice(originSlice []interface{}) {rand.Seed(time.Now().UnixNano())for len(originSlice) > 0 {last := len(originSlice)randIndex := rand.Intn(last)originSlice[last - 1], originSlice[randIndex] = originSlice[randIndex], originSlice[last - 1]originSlice = originSlice[:last-1]}
}

创建一个test_test.go文件

package testFuncimport ("testing"
)//*testing.T
// The other reporting methods, such as the variations of Log and Error,
// may be called simultaneously from multiple goroutines.
func TestRandSlice(t *testing.T){slice := []interface{}{1, 2, 3, 4, 5, 6, 7, 8}RandSlice(slice)t.Errorf("预期不足 %v", slice)
}

1.2 表组测试

所谓表组测试,就是用for循环去做测试,当一次测试结果失败的时候,应该继续还是退出,前面用的是t.Errorf测试失败不会退出,会在最后的时候输出。

总结几个方法的含义:

方法 含义
Fail 记录失败信息,然后继续执行后续用例
FailNow 记录失败信息,所有测试终止
SkipNow 不会记录失败的用例信息,然后中止测试
Skip 记录失败信息,中断后续测试
Skipf 比前面多一个格式化输出
Log 输出错误日志,默认不输出成功的用例,不会中断
Logf 不会中断
Error Log + Fail ,不会中断
Errorf 同上
Fatal Log + FailNow, 中断
Fatalf 相当于Logf + FailNow

1.3 test main

上面的测试用例都是基于函数,方法等无需 stepup/teardown 机制。下面介绍另一种测试方法

func TestMain(t *testing.M){// step up 初始化工作code := m.Run()//tearDownos.Exit(code)
}

1.3 覆盖测试

Go提供分析单元测试覆盖率(触发运行的被测试diamante的代码行数占所有代码行数的比例,成为测试覆盖率)的工具,使用如下。

到达有_test结尾的目录下:

//执行
go test -v -coverprofile=c.out### 输出:
###=== RUN   TestRandSlice
###--- PASS: TestRandSlice (0.00s)
###PASS
###coverage: 75.0% of statements
###ok      gitee.hill.com/mygo/addvance/testFunc   0.664sgo tool cover -html=c.out -o=tag.html

执行最后一句后,当前目录会多一个html文件

小结
  • 单元测试方法必须导出。
  • 单元测试文件名以_test结尾。
  • 单元测试函数名以Test_开头。
  • go test会自动忽略以“_” 或者“.开头的文件。
  • build, intsall 也会自动忽略测试文件。

2. 基准测试

基准测试(benchmarking)是一种测试和评估软件性能的活动。如何判断一段程序的性能,唯有从两个方面去考量,一个是执行性能,一个是空间性能benchmarking可以很好的帮助我们找到一段代码的性能水平,Let’s get it。

在Go中,基准测试方法一般以BenchmarkXXX开头,并且通过go test命令,加上-bench来执行。

这里还是测试前面那一段打乱slice的代码,编写基准测试方法。

func BenchmarkRandSlice(b *testing.B) {slice := []interface{}{1, 2, 3, 4, 5, 6, 7, 8}for i := 0; i < b.N; i++ {RandSlice(slice)}
}

这里循环的i<b.N,表示会循环这么多次

//终端输入:
shell> go test -bench=. -run=none // go test 默认执行单元测试,防止单元测试结果妨碍基准测试, 这里用none代替
//输出:
/*goos: windows
goarch: amd64
pkg: gitee.hill.com/mygo/addvance/testFunc
cpu: Intel(R) Core(TM) i5-8400 CPU @ 2.80GHz
BenchmarkRandSlice-6       18186             65075 ns/op
PASS
ok      gitee.hill.com/mygo/addvance/testFunc   2.253s*/

函数后面的-6表示GOMAXPROCES, 1秒执行了18133次,每次循环时65271 ns。 这里默认测试时一秒,也可以用-benchtime指定时间如:go test -benchtime=3s

注意的时:

  • 如果在基准测试的时候,前面初始化代码会有阻塞,或者加载时长比较长的时候,可以在想要测试语句加上:

    func BenchmarkResetTimer(b *testing.B){...//初始化代码 ,会耗时b. ResetTimer()...//真正想要测试的代码
    }
    
  • 要并行测试性能的时候,用RunParallel函数

2.1 分析代码

  1. 性能分析

    分析intstring方法的性能

    var num = 10
    func BenchmarkI2S1(b *testing.B) {for i := 0; i < b.N; i++ {fmt.Sprintf("%d", num)}
    }func BenchmarkI2S2(b *testing.B) {for i := 0; i < b.N; i++ {strconv.Itoa(num)}
    }func BenchmarkI2S3(b *testing.B) {for i := 0; i < b.N; i++ {strconv.FormatInt(int64(num), 10)}
    }
    

    输出:

    shell> go test -bench=. -run=none
    goos: windows
    goarch: amd64
    pkg: gitee.hill.com/mygo/addvance/testFunc
    cpu: Intel(R) Core(TM) i5-8400 CPU @ 2.80GHz
    BenchmarkI2S1-6         16794162                70.59 ns/op
    BenchmarkI2S2-6         448155502                2.664 ns/op
    BenchmarkI2S3-6         461658669                2.616 ns/op
    PASS
    ok      gitee.hill.com/mygo/addvance/testFunc   4.612s
    

    由结果可以看到strconv.FormaInt性能最高,其次是strconv.Itoa,最后是fmt.Sprintf(), 为了分析更深层次的原因,用benchmem来分析

    输出:

    BenchmarkI2S1-6         16933725                70.90 ns/op            2 B/op          1 allocs/op
    BenchmarkI2S2-6         459021082                2.668 ns/op           0 B/op          0 allocs/op
    BenchmarkI2S3-6         437075035                2.739 ns/op           0 B/op          0 allocs/op
    

    obviously,多了allocs/op, B/op这两列数据,前者的意思是每次操作分配的B数,后者表示每次操作从堆上分配内存的次数。可以看到BenchmarkI2S1比其他每次操作多2B数据,每次操作分配内存次数比其他多1次。当然,在程序中,我们不能一味的只看性能,当然在可用性和复用性允许的情况下,性能需要做出一些让步。

  2. pprof

在上面的例子中,只能查看函数的执行时间,不能进一步的分析程序。要用到go test的一些其他的参数,例如:

  • -bench regexp:regexp 可以是任何正则表达式,表示需要运行的函数
  • -benchmem:输出内容包含内存分配统计信息
  • -benchtime:指定单个测试累积耗时上线,默认1s
  • -cpuprofile out:输出cpu profile到指定文件,pprof查看
  • -memprofile out:输出内存profile到指定文件,pprof查看

基准测试输出cpu信息到文件:

go test -bench=. -run=none -benchmem -cpuprofile cpu.prof

输出的文件,不能直接从查看,需要用pprof来查看:

go tool pprof test_test.go cpu.prof

之后会进入一个交互模式:

test_test.go: parsing profile: unrecognized profile format
Fetched 1 source profiles out of 2
Type: cpu
Time: Aug 9, 2021 at 8:22pm (CST)
Duration: 4.42s, Total samples = 4.30s (97.23%)
Entering interactive mode (type "help" for commands, "o" for options)
(pprof)

输入Top100看看CPU情况:

(pprof) top100
Showing nodes accounting for 4.17s, 96.98% of 4.30s total
Dropped 28 nodes (cum <= 0.02s)flat  flat%   sum%        cum   cum%1.39s 32.33% 32.33%      2.33s 54.19%  strconv.FormatInt0.93s 21.63% 53.95%      0.93s 21.63%  strconv.small0.31s  7.21% 61.16%      1.45s 33.72%  gitee.hill.com/mygo/addvance/testFunc.BenchmarkI2S30.20s  4.65% 65.81%      1.50s 34.88%  gitee.hill.com/mygo/addvance/testFunc.BenchmarkI2S20.13s  3.02% 68.84%      0.20s  4.65%  runtime.mallocgc0.12s  2.79% 71.63%      0.51s 11.86%  fmt.(*pp).doPrintf0.12s  2.79% 74.42%      0.39s  9.07%  fmt.(*pp).printArg0.11s  2.56% 76.98%      0.20s  4.65%  fmt.(*fmt).fmtInteger0.11s  2.56% 79.53%      1.30s 30.23%  strconv.Itoa (inline)0.10s  2.33% 81.86%      0.10s  2.33%  runtime.memmove0.08s  1.86% 83.72%      0.22s  5.12%  fmt.(*pp).free0.08s  1.86% 85.58%      0.13s  3.02%  sync.(*Pool).Get0.08s  1.86% 87.44%      0.14s  3.26%  sync.(*Pool).Put0.07s  1.63% 89.07%      0.27s  6.28%  fmt.(*pp).fmtInteger0.06s  1.40% 90.47%      0.22s  5.12%  fmt.newPrinter0.05s  1.16% 91.63%      1.26s 29.30%  fmt.Sprintf0.05s  1.16% 92.79%      0.05s  1.16%  runtime.procPin0.03s   0.7% 93.49%      0.03s   0.7%  runtime.acquirem (inline)0.03s   0.7% 94.19%      0.03s   0.7%  runtime.nextFreeFast (inline)0.03s   0.7% 94.88%      0.26s  6.05%  runtime.slicebytetostring0.02s  0.47% 95.35%      0.03s   0.7%  fmt.(*fmt).init (inline)0.02s  0.47% 95.81%      0.09s  2.09%  fmt.(*fmt).pad0.02s  0.47% 96.28%      0.07s  1.63%  sync.runtime_procPin0.01s  0.23% 96.51%      1.29s 30.00%  gitee.hill.com/mygo/addvance/testFunc.BenchmarkI2S10.01s  0.23% 96.74%      0.03s   0.7%  runtime.bgscavenge.func20.01s  0.23% 96.98%      0.09s  2.09%  sync.(*Pool).pin0     0% 96.98%      0.07s  1.63%  fmt.(*buffer).write (inline)0     0% 96.98%      0.05s  1.16%  runtime.systemstack0     0% 96.98%      4.24s 98.60%  testing.(*B).launch0     0% 96.98%      4.24s 98.60%  testing.(*B).runN

pprof 实际上有两种应用方式,另一种就是

 go func() {log.Debug(http.ListenAndServe(":6060", nil))}()

go tool pprof http://localhost:6060/debug/pprof/profile?seconds=60
然后就可以通过网页去访问啦~

pprof阅读还有另外一种方式:

就是在Goland自带的工具去分析profile文件,可以生成火焰图、调用树、方法列表

FAQ:
1、在测试Web服务的时候,也会有net/http包,所以在进行压测的时候,不知道哪个端口才是用来做压测的,这个时候就要具体导入net/http/pprof包(3小时的教训)

Go语言-测试与性能分析相关推荐

  1. 《C++应用程序性能优化::第二章C++语言特性的性能分析》学习和理解

    <C++应用程序性能优化::第二章C++语言特性的性能分析>学习和理解 说明:<C++应用程序性能优化> 作者:冯宏华等 2007年版.最近出了新版,看了目录,在前面增加了一章 ...

  2. Go语言——测试与性能

    篇幅较长,建议先收藏 文章目录 测试与性能 1. 测试 1.1 单元测试 1. 第一个测试函数 2. 表组测试 3. 模拟测试 △ 1. HTTP mock 2. 数据库 mock mysql:go- ...

  3. golang笔记09--go语言测试与性能调优

    golang笔记09--go语言测试与性能调优 1 介绍 2 测试与性能调优 2.1 测试介绍 2.2 代码覆盖率和性能测试 2.3 使用pprof进行性能调优 2.4 测试http服务器(上) 2. ...

  4. [SQL Server]用于压力测试和性能分析的两个支持实用工具[转]

    微软有两个不提供支持服务的SQL Server压力测试和性能分析工具.具体看微软知识库: http://support.microsoft.com/?kbid=887057 分别是: Read80Tr ...

  5. 『每周译Go』Go 语言的 goroutine 性能分析

    本文档最后一次更新时所用的 Go 版本是 1.15.6,但是大多数情况下,新老版本都适用. 描述 Go 运行时在一个称为 allgs 简单切片追踪所有的 goroutines.这里面包含了活跃的和死亡 ...

  6. 怎样对流媒体进行压力测试_对node工程进行压力测试与性能分析「干货」

    作者:小黎 转发链接:https://mp.weixin.qq.com/s/WBe7ZLoqFD9UqNusnv_IDA 前言 在系统上线前,为了看下系统能承受多大的并发和并发下的负载情况,常常会先进 ...

  7. 对node工程进行压力测试与性能分析

    在系统上线前,为了看下系统能承受多大的并发和并发下的负载情况,进行了一轮压测.在压测过程中,发现服务器的cpu飚的的非常高,而tps,接口耗时.服务可用等都是正常的,卧槽,这就奇了怪了,自己想了半天也 ...

  8. Go 语言编程 — Profiling 性能分析

    目录 文章目录 目录 Profiling runtime MemStat GC pprof trace Profiling Golang 提供了友好的工程化支持,其中之一就是 Profiling(分析 ...

  9. 数据切分——Mysql分区表的建立及性能分析

    Mysql的安装方法可以参考: http://blog.csdn.net/jhq0113/article/details/43812895 Mysql分区表的介绍可以参考: http://blog.c ...

  10. 你是如何测试电机性能的?

    在自动化测试日渐成熟的阶段,你是怎么测试电机的性能的呢?纳米软件自主研发的NSAT-7000电机自动测试系统告别了传统测试效率低,测试速度慢等弊端,接下来我们先认识一下电机. 电机的分类 电机是依据电 ...

最新文章

  1. 不试过你怎么知道?开博第一篇(本人菜鸟也,高手可以飘过)
  2. 仓储rfid文件_RFID智能仓储管理技术浅析之RFID电子标签的使用
  3. MFC六大核心机制之三:动态创建
  4. java五子棋代码详解_代码详解:Java和Valohai的深度学习之旅
  5. android adapter hierarchy
  6. Codeigniter基础
  7. @RequestParam接收解析不到 POST 提交的 数据
  8. Postfix:邮件系统常见错误代码解释
  9. element表格固定某一行_elementui 表格 固定列+合计行
  10. 客服机器人代码_企业微信群机器人如何快速集成?无需开发连接微信公众号,表单系统,钉钉,推广,CRM,客服系统和数据库...
  11. 西门子224XP源码,包括pcb,原理图,224xp源码
  12. mac读取ntfs移动硬盘
  13. BI报表工具FineReport的使用2【帆软聚合报表设计,最常用功能】
  14. camera驱动电源配置_[ROS] 安装 USB Camera 驱动并调用
  15. 三羊献瑞-第六届蓝桥杯省赛
  16. 吴恩达机器学习课程06——正则化
  17. 「我只是认真」聊聊工匠情怀
  18. 小飞鱼通达二开 企业微信与通达OA的另一种集成方式(图文)
  19. Unity3D游戏开发之仿仙剑奇侠传仙灵岛机关的实现
  20. leetcode1024. 视频拼接

热门文章

  1. 用spss做哑变量(含spss安装教程)
  2. 基于jsp与基于java有什么区别_JSP和HTML之间有什么区别
  3. strcmp函数的实现
  4. 电影购票c语言程序,C语言电影购票系统小样
  5. android多音字转拼音工具分装
  6. easydarwin 安装_win10安装EasyDarwin
  7. 探讨职业化的表达方式
  8. 恒定湿热试验条件及标准-环境试验中的湿热试验报告
  9. Altium Designer使用-----智能pdf输出装配图
  10. 绿联USB网卡的使用记录