Go 语言性能测试 - 入门篇

Go 语言性能测试 - 入门篇

Go 语言是非常高效的,在处理并发请求时的性能表现非常出色,在某些考虑性能因素的场景下,我们需要进行性能测试,Go语言提供了用于性能测试的 testing.B 框架,本篇就介绍下Go语言的性能测试的基本做法。

参考阅读

基本格式

Go 性能测试的函数基本格式:

func BenchmarkXxx(*testing.B)

Benchmark测试通过 go test 命令来启动,通过 -bench 这样一个标签来标明是Benchmark测试,启动后会按照顺序执行。

下面我们有一个简单的例子,这里只有一个输出hello的方法:

benchmark.go

func Hello() {
	fmt.Sprintf("hello")
}

按照规范,我们需要一个benchmark_test.go的文件来测试。

benchmark_test.go

func BenchmarkHello(b *testing.B) {
	for i := 0; i < b.N; i++ {
		Hello()
	}
}

这里的b.N是必须运行的次数,在benchmark运行期间,这个数值会调整,直到可以稳定地评估出性能。

$ go test -bench BenchmarkHello
goos: darwin
goarch: amd64
pkg: demo/benchmark
BenchmarkHello-4   	20000000	        59.4 ns/op
PASS
ok  	demo/benchmark	1.264s

结果表示以 59.4 ns/op 的速度运行了 20000000 次,耗时 1.264 秒

有些时候,我们想多跑几组,对比下性能测试结果是否稳定,我们可以加一些参数:

$ go test -test.bench=BenchmarkHello -count=5
goos: darwin
goarch: amd64
pkg: demo/benchmark
BenchmarkHello-4   	20000000	        59.5 ns/op
BenchmarkHello-4   	20000000	        58.4 ns/op
BenchmarkHello-4   	20000000	        59.7 ns/op
BenchmarkHello-4   	20000000	        58.7 ns/op
BenchmarkHello-4   	20000000	        59.0 ns/op
PASS
ok  	demo/benchmark	6.239s

这里我们跑了五组测试,性能还是相对比较稳定,实际有很多参数是可以加的,整理如下:

-test.bench patten: 只跑那些性能测试用例

-test.benchmem : 是否在性能测试的时候输出内存情况

-test.benchtime t : 性能测试运行的时间,默认是1s

-test.cpuprofile cpu.out : 是否输出cpu性能分析文件

-test.memprofile mem.out : 是否输出内存性能分析文件

-test.parallel n : 性能测试的程序并行cpu数,默认等于GOMAXPROCS。

-test.timeout t : 如果测试用例运行时间超过t,则抛出panic

-test.cpu 1,2,4 : 程序运行在哪些CPU上面,使用二进制的1所在位代表。

-test.short : 将那些运行时间较长的测试用例运行时间缩短

指定cpu运行的时候,我们需要注意的是,比如设置4并不是指在第四个CPU上运行,根据二进制0100可知,是在第三个CPU上运行。这个和nginx的worker_cpu_affinity配置相似。具体请参考:worker_cpu_affinity

并行测试

在某些时候,我们想并行地运行测试用例,我们可以用 b.RunParallel 方法来启动,这时候最好启用 -cpu 参数

func BenchmarkTemplateParallel(b *testing.B) {
	templ := template.Must(template.New("test").Parse("Hello, {{.}}!"))
	b.RunParallel(func(pb *testing.PB) {
		var buf bytes.Buffer
		for pb.Next() {
			buf.Reset()
			templ.Execute(&buf, "World")
		}
	})
}

运行测试用例输出:

$ go test -test.bench=BenchmarkTemplateParallel -cpu 1,2,4
goos: darwin
goarch: amd64
pkg: demo/benchmark
BenchmarkTemplateParallel     	 1000000	      1247 ns/op
BenchmarkTemplateParallel-2   	 2000000	       626 ns/op
BenchmarkTemplateParallel-4   	 2000000	       615 ns/op
PASS
ok  	demo/benchmark	5.035s

我们制定指定在1,2,3号CPU上并行运行,输出结果显示了在三个CPU上运行的速度情况。

我们考虑写如下的测试代码:

func TestFoo(t *testing.T) {
	t.Run("A=1", func(t *testing.T) { fmt.Println("A=1") })
	t.Run("A=2", func(t *testing.T) { fmt.Println("A=2") })
	t.Run("B=1", func(t *testing.T) { fmt.Println("B=1") })
}

这里我们有三个用例,逐一运行,但是重复代码比较多,在上篇Go 语言单元测试(顶部有链接)中提到,最好使用 Table Driven 的形式,那我们来改造下:

func TestGroupedParallel(t *testing.T) {
	tests := []struct {
		Name string
	}{
		{
			Name: "A=1",
		},
		{
			Name: "A=2",
		},
		{
			Name: "B=1",
		},
	}
	for _, tc := range tests {
		tc := tc // capture range variable
		t.Run(tc.Name, func(t *testing.T) {
			t.Parallel()
		})
	}
}

我们通过执行看结果:

$ go test -run TestGroupedParallel -v
=== RUN   TestGroupedParallel
=== RUN   TestGroupedParallel/A=1
=== PAUSE TestGroupedParallel/A=1
=== RUN   TestGroupedParallel/A=2
=== PAUSE TestGroupedParallel/A=2
=== RUN   TestGroupedParallel/B=1
=== PAUSE TestGroupedParallel/B=1
=== CONT  TestGroupedParallel/A=1
=== CONT  TestGroupedParallel/B=1
=== CONT  TestGroupedParallel/A=2
--- PASS: TestGroupedParallel (0.00s)
    --- PASS: TestGroupedParallel/A=1 (0.00s)
    --- PASS: TestGroupedParallel/B=1 (0.00s)
    --- PASS: TestGroupedParallel/A=2 (0.00s)
PASS
ok  	demo/benchmark	0.006s

我们看到三个用例都被执行,代码看起来更优雅。

最后,我们介绍下可以嵌套并行地执行测试用例,如下:

func TestTeardownParallel(t *testing.T) {
    // This Run will not return until the parallel tests finish.
    // <setup code here>
    t.Run("group", func(t *testing.T) {
        t.Run("Test1", parallelTest1)
        t.Run("Test2", parallelTest2)
        t.Run("Test3", parallelTest3)
    })
    // <tear-down code>
}

总结

Go 语言的性能测试其实是一个比较复杂的过程,要结合很多参数,比如 -cpu-membench 等等来深入分析性能细节,这里我们可以考虑采用这些辅助工具来实现,这篇只是一个开始,希望对大家有帮助。

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值