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

  • 1 介绍
  • 2 测试与性能调优
    • 2.1 测试介绍
    • 2.2 代码覆盖率和性能测试
    • 2.3 使用pprof进行性能调优
    • 2.4 测试http服务器(上)
    • 2.5 测试http服务器(下)
    • 2.6 生成文档和示例代码
  • 3 注意事项
  • 4 说明

1 介绍

本文继上文 golang笔记08–go语言错误处理和资源管理, 进一步了解 go语言的错误处理和资源管理,以及相应注意事项。
具体包括 : 测试介绍 、代码覆盖率和性能测试、使用pprof进行性能调优、p测试http服务器(上)、测试http服务器(下)、生成文档和示例代码 等内容。

2 测试与性能调优

2.1 测试介绍

传统测试 vs 表格测试:

传统测试 表格测试
测试数据和测试逻辑混在一起 分离的测试数据和测试逻辑
出错信息不明确 明确的出错信息
一旦一个数据出错测试全部结束 可以部分失败

两者测试代码结构如下图所示(左边传统、右边表格):

vim triangle.go
package mainimport ("fmt""math"
)func triangle() {var a, b int = 3, 4c := calTriangle(a, b)fmt.Println(c)
}func calTriangle(a, b int) int {var c intc = int(math.Sqrt(float64(a*a + b*b)))return c
}vim triangle_test.go
package mainimport "testing"func TestTriangle(t *testing.T) {tests := []struct{ a, b, c int }{{3, 4, 5},{5, 12, 13},{8, 15, 17},{12, 35, 37},{3000, 4000, 5000},//{3000, 4000, 5001},}for _, tt := range tests {if actual := calTriangle(tt.a, tt.b); actual != tt.c {t.Errorf("calTriangle(%d, %d); got %d; expected %d", tt.a, tt.b, tt.c, calTriangle(tt.a, tt.b))}}
}
输出:
=== RUN   TestTriangle
--- PASS: TestTriangle (0.00s)
PASS
出错输出:
=== RUN   TestTriangletriangle_test.go:16: calTriangle(3000, 4000); got 5001; expected 5000
--- FAIL: TestTriangle (0.00s)
FAIL

2.2 代码覆盖率和性能测试

IDEA 中,对于testing类型的程序,可以直接通过查看 Run TestTriangle in lear… with Coverage来查看测试代码的覆盖率(也可以根据需要选择CPU 或者 Memory Profiler),如下图所示:

vim triangle.go
package mainimport ("fmt""math"
)func triangle() {var a, b int = 3, 4c := calTriangle(a, b)fmt.Println(c)
}func calTriangle(a, b int) int {var c intc = int(math.Sqrt(float64(a*a + b*b)))return c
}vim triangle_test.go
package mainimport "testing"func TestTriangle(t *testing.T) {tests := []struct{ a, b, c int }{{3, 4, 5},{5, 12, 13},{8, 15, 17},{12, 35, 37},{3000, 4000, 5000},//{3000, 4000, 5001},}for _, tt := range tests {if actual := calTriangle(tt.a, tt.b); actual != tt.c {t.Errorf("calTriangle(%d, %d); got %d; expected %d", tt.a, tt.b, tt.c, calTriangle(tt.a, tt.b))}}
}func BenchmarkTriangle(b *testing.B) {a1, b1, c1 := 30000, 40000, 50000for i := 0; i < b.N; i++ {actual := calTriangle(a1, b1)if actual != c1 {b.Errorf("calTriangle(%d, %d); got %d; expected %d", a1, b1, c1, calTriangle(a1, b1))}}
}TestTriangle 输出:
=== RUN   TestTriangle
--- PASS: TestTriangle (0.00s)
PASSBenchmarkTriangle 输出:
goos: linux
goarch: amd64
pkg: learngo/chapter9/9.1
BenchmarkTriangle
BenchmarkTriangle-4     1000000000           0.285 ns/op 【执行了1000000000此操作,平均每次操作0.285 ns】
PASS除了上述 ide直接执行测试用例外,也可以通过命令行执行测试用例:
1) 在测试用例当前目录执行go test就可以执行测试用例
chapter9/9.1$ go test
2)通过 coverprofile=c.out 可以输出测试覆盖率到 c.out 文件
chapter9/9.1$ go test -coverprofile=c.out
PASS
coverage: 50.0% of statements
ok      learngo/chapter9/9.1    0.002s
可以进一步通过 chapter9/9.1$ go tool cover -html c.out 查看覆盖率信息
3)命令行执行benchmark
go test -bench .

2.3 使用pprof进行性能调优

本案例使用benchmark 的 cpuprofile,结合寻找最长不重复子串来逐步优化程序性能,案例中的 web 图标需要依赖 graphviz 。

具体思路: -cpuprofile获取性能数据 --》go tool pprof查看性能数据 --》根据web图分析慢在哪里 --》优化代码

vim 9.2.go
package mainimport "fmt"func lengthOfNonRepeatSubStrOld(s string) int {lastOccurred := make(map[rune]int)start := 0maxLength := 0for i, ch := range []rune(s) {if lastI, ok := lastOccurred[ch]; ok && lastI >= start {start = lastI + 1}if i-start+1 > maxLength {maxLength = i - start + 1}lastOccurred[ch] = i}return maxLength
}var lastOccurred = make([]int, 0xffff) //假定中文字的最大值为65535=0xffff
func lengthOfNonRepeatSubStr(s string) int {// lastOccurred := make([]int, 0xffff) //假定中文字的最大值为65535=0xfffffor i := range lastOccurred {lastOccurred[i] = -1}start := 0maxLength := 0for i, ch := range []rune(s) {if lastI := lastOccurred[ch]; lastI != -1 && lastI >= start {start = lastI + 1}if i-start+1 > maxLength {maxLength = i - start + 1}lastOccurred[ch] = i}return maxLength
}func main() {fmt.Println("this chapter 9.3")str := "黑化肥挥发发灰会花飞灰化肥挥发发黑会飞花"fmt.Printf("%s lengthOfNonRepeatSubStr = %d ", str, lengthOfNonRepeatSubStr(str))
}vim nonrepeating_test.go
package mainimport "testing"func BenchmarkSubstr(b *testing.B) {s := "黑化肥挥发发灰会花飞灰化肥挥发发黑会飞花"for i := 0; i < 13; i++ {s = s + s}b.Logf("len(s) = %d", len(s))ans := 8b.ResetTimer()for i := 0; i < b.N; i++ {actual := lengthOfNonRepeatSubStr(s)if actual != ans {b.Errorf("got %d for input %s; "+"expected %d", actual, s, ans)}}
}输出(没有优化):
$ go test -bench . -cpuprofile cpu.out
goos: linux
goarch: amd64
pkg: learngo/chapter9/9.2
BenchmarkSubstr-4            178           6580817 ns/op
--- BENCH: BenchmarkSubstr-4nonrepeating_test.go:10: len(s) = 491520nonrepeating_test.go:10: len(s) = 491520nonrepeating_test.go:10: len(s) = 491520
PASS
ok      learngo/chapter9/9.2    2.016s
输出(优化rune字符串功能):
goos: linux
goarch: amd64
pkg: learngo/chapter9/9.2
BenchmarkSubstr-4            426           2756128 ns/op
--- BENCH: BenchmarkSubstr-4nonrepeating_test.go:10: len(s) = 491520nonrepeating_test.go:10: len(s) = 491520nonrepeating_test.go:10: len(s) = 491520
PASS
ok      learngo/chapter9/9.2    1.612s
输出(将make 放在最外层):
goos: linux
goarch: amd64
pkg: learngo/chapter9/9.2
BenchmarkSubstr-4            486           2332484 ns/op
--- BENCH: BenchmarkSubstr-4nonrepeating_test.go:10: len(s) = 491520nonrepeating_test.go:10: len(s) = 491520nonrepeating_test.go:10: len(s) = 491520
PASS
ok      learngo/chapter9/9.2    1.511s也可以通过命令行来测试cpu性能:
go test -bench . -cpuprofile cpu.out
可以通过tool进一步查看cpu.out 信息,  go tool pprof cpu.out ->交互终端输出 web就会显示出测试时间关系图,结果如下(需要安装graphviz):


从图中可以看到 2 个map 和 一个rune 发的时间较多,rune 暂时不适合优化,但是 map 可以适当优化为对应的数组

优化后的耗时图如下,课件优化后主要时间发在 stringtoslicerune 上了,当然还有少量时间发在 makeslice 上面(0.05s):

进一步将make 移到函数外面,可以发现压测时间进一步略微减少了,此时结果中已经没有 makeslice 了:

2.4 测试http服务器(上)

本案例基于 golang笔记08–go语言错误处理和资源管理 中的 filelisting 来测试 http 服务,具体示例如下:

chapter8/8.3$ tree -L 2
.
├── filelisting
│   └── handler.go
├── web.go
└── web_test.govim web_test.go
package mainimport ("io/ioutil""net/http""net/http/httptest""strings""testing"
)func errPanic(_ http.ResponseWriter,_ *http.Request) error {panic(123)
}var tests = []struct {h       appHandlercode    intmessage string
}{{errPanic, 500, "Internal Server Error"},
}func TestErrWarpper(t *testing.T) {for _, tt := range tests {f := errWarpper(tt.h)response := httptest.NewRecorder()request := httptest.NewRequest(http.MethodGet, "http://www.imooc.com", nil)f(response, request)b, _ := ioutil.ReadAll(response.Body)body := strings.Trim(string(b), "\n") // web默认返回有个换行符,此处需要去掉才能正确匹配if response.Code != tt.code || body != tt.message {t.Errorf("expect (%d, %s); got (%d, %s)", tt.code, tt.message, response.Code, body)}}
}
输出:
=== RUN   TestErrWarpper
--- PASS: TestErrWarpper (0.00s)
PASS

2.5 测试http服务器(下)

http 测试通常包括两种方式:
1) 通过使用假的Request/Response
2)通过起服务器

本案例对上述 2.4 中的测试 case 进一步丰富, 使之能够测试更多异常情况,具体内容如下:

vim web2_test.go
package mainimport ("errors""fmt""io/ioutil""net/http""net/http/httptest""os""strings""testing"
)func errPanic2(_ http.ResponseWriter,_ *http.Request) error {panic(123)
}type testingUserError stringfunc (e testingUserError) Error() string {return e.Message()
}func (e testingUserError) Message() string {return string(e)
}func errUserError(_ http.ResponseWriter,_ *http.Request) error {return testingUserError("user error")
}func errNotFound(_ http.ResponseWriter,_ *http.Request) error {return os.ErrNotExist
}func errNoPermission(_ http.ResponseWriter,_ *http.Request) error {return os.ErrPermission
}func errUnknown(_ http.ResponseWriter,_ *http.Request) error {return errors.New("unknown error")
}func noError(writer http.ResponseWriter,_ *http.Request) error {fmt.Fprintln(writer, "no error")return nil
}var tests = []struct {h       appHandlercode    intmessage string
}{{errPanic2, 500, "Internal Server Error"},{errUserError, 400, "user error"},{errNotFound, 404, "Not Found"},{errNoPermission, 403, "Forbidden"},{errUnknown, 500, "Internal Server Error"},{noError, 200, "no error"},
}func TestErrWarpper2(t *testing.T) {for _, tt := range tests {f := errWarpper(tt.h)response := httptest.NewRecorder()request := httptest.NewRequest(http.MethodGet, "http://www.imooc.com", nil)f(response, request)b, _ := ioutil.ReadAll(response.Body)body := strings.Trim(string(b), "\n") // web默认返回有个换行符,此处需要去掉才能正确匹配if response.Code != tt.code || body != tt.message {t.Errorf("expect (%d, %s); got (%d, %s)", tt.code, tt.message, response.Code, body)}}
}func TestErrWarpperInserver(t *testing.T) {for _, tt := range tests {f := errWarpper(tt.h)server := httptest.NewServer(http.HandlerFunc(f))resp, _ := http.Get(server.URL)b, _ := ioutil.ReadAll(resp.Body)body := strings.Trim(string(b), "\n") // web默认返回有个换行符,此处需要去掉才能正确匹配if resp.StatusCode != tt.code || body != tt.message {t.Errorf("expect (%d, %s); got (%d, %s)", tt.code, tt.message, resp.StatusCode, body)}}
}
输出(TestErrWarpper2):
=== RUN   TestErrWarpper2
gopm WARN error occurred handling request: user error
gopm WARN error occurred handling request: file does not exist
gopm WARN error occurred handling request: permission denied
gopm WARN error occurred handling request: unknown error
--- PASS: TestErrWarpper2 (0.00s)
PASS
输出(TestErrWarpperInserver):
=== RUN   TestErrWarpperInserver
gopm WARN error occurred handling request: user error
gopm WARN error occurred handling request: file does not exist
gopm WARN error occurred handling request: permission denied
gopm WARN error occurred handling request: unknown error
--- PASS: TestErrWarpperInserver (0.00s)
PASS

2.6 生成文档和示例代码

go语言中可以通过go doc 查看代码的包和对应的函数,也可以查看系统文档功能。

1) 通过 go doc 查看queue 的文档信息
chapter6/queue$ go doc
package queue // import "learngo/chapter6/queue"type Queue []interface{}2)通过 go doc 查看具体数据结构信息
chapter6/queue$ go doc Queue
go doc Queue
package queue // import "."type Queue []interface{}func (q *Queue) IsEmpty() bool
func (q *Queue) Pop() interface{}
func (q *Queue) Push(v interface{})3) 查看go库函数说明文档
$ go doc fmt.println4)输出godoc 的 http 服务器
$ godoc -http :6060
若在项目目录下起,则会包括项目的文档信息5) 若需要添加文档,则直接将注释卸载函数上一或行即可,例如
// this is push interface
func (q *Queue) Push(v interface{}) {*q = append(*q, v)
}6) go 中也可以新建Example示例代码,并能检查相关的结果,如果生成对应的godoc,则会生成对应的 Example的 Code 和 Output,具体代码如下
func ExampleQueue_Pop() {q := Queue{1}q.Push(2)q.Push(3)fmt.Println(q.Pop())fmt.Println(q.Pop())fmt.Println(q.IsEmpty())fmt.Println(q.Pop())fmt.Println(q.IsEmpty())// Output:// 1// 2// false// 3// true
}

通过本地 godoc 起一个文档查询服务器:

3 注意事项

  1. 查看 cpuprofile 信息时需要安装 graphviz

    apt install graphviz
    

4 说明

  1. 软件环境
    go版本:go1.15.8
    操作系统:Ubuntu 20.04 Desktop
    Idea:2020.01.04
  2. 参考文档
    由浅入深掌握Go语言 --慕课网
    go 语言编程 --许式伟
    graphviz download

golang笔记09--go语言测试与性能调优相关推荐

  1. golang学习笔记——测试与性能调优

    测试 longestString/longestString.go package main//Nonrepeating : method of double points(双指针法) func No ...

  2. 8、go语言:测试与性能调优

    1.测试: 程序应该多做测试,少做调试 传统测试 VS 表格驱动测试(go语言使用) //传统测试 @Test public void testAdd(){assertEquals(3,add(1,2 ...

  3. 《Java性能调优实战》笔记(一)Java编程性能调优、多线程性能优化

    文章目录 一.Java性能调优概述 1.1 性能调优标准 1.2 制定性能调优策略 二.Java编程性能调优 2.1 字符串 2.2 正则表达式 2.3 ArrayList和LinkedList的选择 ...

  4. Jmeter压力测试和性能调优

    1.压力测试的概念 压力测试是模拟实际应用的软硬件环境及用户使用过程的系统负荷,长时间或超大负荷地运行测试软件,来测试被测系统的性能.可靠性.稳定性等 2.压力测试的目的 1)给出系统当前的性能状况 ...

  5. 字节青训营第三课之高质量编程与性能调优实战的笔记和总结

    这是字节青训营第三课:高质量编程与性能调优实战的笔记和总结 概要 准备 尝试使用 test 命令,编写并运行简单测试 尝试使用 -bench参数,对函数进行性能测试 推荐阅读Go代码Review建议. ...

  6. 【JVM 学习笔记 05】:JVM性能调优工具的使用和优化案例

    [JVM 学习笔记 05]:JVM性能调优工具的使用 1. 使用 jstat(命令行工具) 查看线上系统的JVM运行状况 1.1 常用命令 1.2 使用技巧 1.2.1 随着系统运行,每秒钟会在年轻代 ...

  7. java性能调优实战学习笔记

    这是极客时间专栏<java性能调优实战>的部分学习笔记,个人感觉这个专栏内容不深,适合初学者,我只看了编程性能调优和数据库性能调优两块,其他的暂时不打算看了,后续有时间再看吧 有任何问题可 ...

  8. j2ee性能调优之最小化资源压力测试法则

    摘要:我提倡使用最小化资源的方式做一次压力测试,排除大部分浅显的应用问题.最小资源的意思,即在pc环境,使用应用可以运行的最小资源状态下,进行压力测试和性能问题侦测的工作. 前面看到有人讲j2ee的性 ...

  9. 服务器优化:Tomcat、JVM性能调优笔记

    找到Tomcat根目录下的conf目录,修改server.xml文件的内容.对于这部分的调优,我所了解到的就是无非设置一下Tomcat服务器的最大并发数和Tomcat初始化时创建的线程数的设置,当然还 ...

最新文章

  1. 在Ubuntu 14.04 64bit下玩转Qvod快播Linux山寨版本
  2. 打造全球最大规模 Kafka 集群,Uber 的多区域灾备实践
  3. 如何将本地数据库迁移到数据库上?
  4. BZOJ.2716.[Violet3]天使玩偶(CDQ分治 坐标变换)
  5. windows下共享文件夹在Linux下打开
  6. linux 函数 文件校验,Linux中的文件效验命令
  7. How to: Build a Client Application
  8. 心痛!你的快递可能已经被烧毁,13吨快递“无一生还”
  9. 在centos x86_64里编译x32的程序
  10. 计算机专业Java必读书单,高清PDF电子版下载
  11. 可测函数积分的进一步性质
  12. 商业智能,数据仓库,ETL,数仓调度工具informatica介绍手账(二)
  13. 经纬M300赛尔102S航测全流程解析
  14. cocos creator android 真机调试配置密匙
  15. 全文检索第一篇lucene的使用
  16. [算法总结] 中位数+绝对值不等式 AcWing 104. 货仓选址
  17. 采集快手APP的10个经典方法
  18. 实验室风淋系统洁净风淋室
  19. 谈一谈Java中的深拷贝和浅拷贝
  20. 版权和商标对比有哪些不同

热门文章

  1. 双目相机标定理论总结
  2. Oracle 11g_数据导出与导入(14)
  3. 存储大师班 | 浅谈 RDMA 与无损网络
  4. 利用阿里云的API实现动态域名解析
  5. 详细介绍Covariate Shift问题
  6. SAP Cording Block维护客户化字段
  7. Leetcode算法类型训练
  8. Unity 出现error CS0103: The name ‘AssetDatabase‘ does not exist in the current context
  9. 随机变量的分布及其数字特征
  10. Java使用Word的模板引擎 Poi-tl操控导出word文件