golang笔记09--go语言测试与性能调优
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 注意事项
- 查看 cpuprofile 信息时需要安装 graphviz
apt install graphviz
4 说明
- 软件环境
go版本:go1.15.8
操作系统:Ubuntu 20.04 Desktop
Idea:2020.01.04 - 参考文档
由浅入深掌握Go语言 --慕课网
go 语言编程 --许式伟
graphviz download
golang笔记09--go语言测试与性能调优相关推荐
- golang学习笔记——测试与性能调优
测试 longestString/longestString.go package main//Nonrepeating : method of double points(双指针法) func No ...
- 8、go语言:测试与性能调优
1.测试: 程序应该多做测试,少做调试 传统测试 VS 表格驱动测试(go语言使用) //传统测试 @Test public void testAdd(){assertEquals(3,add(1,2 ...
- 《Java性能调优实战》笔记(一)Java编程性能调优、多线程性能优化
文章目录 一.Java性能调优概述 1.1 性能调优标准 1.2 制定性能调优策略 二.Java编程性能调优 2.1 字符串 2.2 正则表达式 2.3 ArrayList和LinkedList的选择 ...
- Jmeter压力测试和性能调优
1.压力测试的概念 压力测试是模拟实际应用的软硬件环境及用户使用过程的系统负荷,长时间或超大负荷地运行测试软件,来测试被测系统的性能.可靠性.稳定性等 2.压力测试的目的 1)给出系统当前的性能状况 ...
- 字节青训营第三课之高质量编程与性能调优实战的笔记和总结
这是字节青训营第三课:高质量编程与性能调优实战的笔记和总结 概要 准备 尝试使用 test 命令,编写并运行简单测试 尝试使用 -bench参数,对函数进行性能测试 推荐阅读Go代码Review建议. ...
- 【JVM 学习笔记 05】:JVM性能调优工具的使用和优化案例
[JVM 学习笔记 05]:JVM性能调优工具的使用 1. 使用 jstat(命令行工具) 查看线上系统的JVM运行状况 1.1 常用命令 1.2 使用技巧 1.2.1 随着系统运行,每秒钟会在年轻代 ...
- java性能调优实战学习笔记
这是极客时间专栏<java性能调优实战>的部分学习笔记,个人感觉这个专栏内容不深,适合初学者,我只看了编程性能调优和数据库性能调优两块,其他的暂时不打算看了,后续有时间再看吧 有任何问题可 ...
- j2ee性能调优之最小化资源压力测试法则
摘要:我提倡使用最小化资源的方式做一次压力测试,排除大部分浅显的应用问题.最小资源的意思,即在pc环境,使用应用可以运行的最小资源状态下,进行压力测试和性能问题侦测的工作. 前面看到有人讲j2ee的性 ...
- 服务器优化:Tomcat、JVM性能调优笔记
找到Tomcat根目录下的conf目录,修改server.xml文件的内容.对于这部分的调优,我所了解到的就是无非设置一下Tomcat服务器的最大并发数和Tomcat初始化时创建的线程数的设置,当然还 ...
最新文章
- 在Ubuntu 14.04 64bit下玩转Qvod快播Linux山寨版本
- 打造全球最大规模 Kafka 集群,Uber 的多区域灾备实践
- 如何将本地数据库迁移到数据库上?
- BZOJ.2716.[Violet3]天使玩偶(CDQ分治 坐标变换)
- windows下共享文件夹在Linux下打开
- linux 函数 文件校验,Linux中的文件效验命令
- How to: Build a Client Application
- 心痛!你的快递可能已经被烧毁,13吨快递“无一生还”
- 在centos x86_64里编译x32的程序
- 计算机专业Java必读书单,高清PDF电子版下载
- 可测函数积分的进一步性质
- 商业智能,数据仓库,ETL,数仓调度工具informatica介绍手账(二)
- 经纬M300赛尔102S航测全流程解析
- cocos creator android 真机调试配置密匙
- 全文检索第一篇lucene的使用
- [算法总结] 中位数+绝对值不等式 AcWing 104. 货仓选址
- 采集快手APP的10个经典方法
- 实验室风淋系统洁净风淋室
- 谈一谈Java中的深拷贝和浅拷贝
- 版权和商标对比有哪些不同
热门文章
- 双目相机标定理论总结
- Oracle 11g_数据导出与导入(14)
- 存储大师班 | 浅谈 RDMA 与无损网络
- 利用阿里云的API实现动态域名解析
- 详细介绍Covariate Shift问题
- SAP Cording Block维护客户化字段
- Leetcode算法类型训练
- Unity 出现error CS0103: The name ‘AssetDatabase‘ does not exist in the current context
- 随机变量的分布及其数字特征
- Java使用Word的模板引擎 Poi-tl操控导出word文件