前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >golang性能测试及优化(上)

golang性能测试及优化(上)

作者头像
机械视角
发布2019-10-23 11:29:16
7690
发布2019-10-23 11:29:16
举报
文章被收录于专栏:Tensorbytes

以斐波那契数列作为测试用例:

代码语言:javascript
复制
package fib

func Fib(n int) int {
	switch n {
	case 0:
		return 0
	case 1:
		return 1
	case 2:
		return 2
	default:
		return Fib(n-1) + Fib(n-2)
	}
}

性能测试Benchmarks的用法和单元测试类似,也是用的testing,性能测试用*testing.B代替*testing.T

单元测试日志

方法

说明

Log

打印日志,同时结束测试

Logf

格式化打印日志,同时结束测试

Error

打印错误日志,同时结束测试

Errorf

格式化打印错误日志,同时结束测试

Fatal

打印致命日志,同时结束测试

Fatalf

格式化打印致命日志,同时结束测试

以上方法testing.B也可以使用。

编写性能测试fib_test.go:

代码语言:javascript
复制
package fib

import (
	"testing"
)

func BenchmarkFib20(b *testing.B) {
	for n := 0; n < b.N; n++ {
		Fib(20) // run the Fib function b.N times
	}
}

运行go test -bench=., 结果:

goos: linux goarch: amd64 BenchmarkFib20-8 50000 37932 ns/op PASS

Windows 下使用 go test 命令行时,-bench=.应写为-bench="."

参数-cpu表示开启CPU的核数,通过GOMAXPROCS进行控制,比如-cpu=1,2,4表示分别开启1核、2核、4核进行测试:

$ go test -bench=. -cpu=1,2,4 goos: linux goarch: amd64 BenchmarkFib20 50000 36877 ns/op BenchmarkFib20-2 50000 36843 ns/op BenchmarkFib20-4 30000 36231 ns/op

参数-benchtime可以让我们自己控制测试的时间,比如:

$ go test -bench=. -benchtime=3s goos: linux goarch: amd64 BenchmarkFib20-8 100000 46070 ns/op PASS ok _/home/shikanon/study/fib 5.018s

你会发现总的运行时间并不是3s,而且不同的运行其时间会有差异,这主要是因为由于GC活动、后台运行程序、内存位置、CPU的调整频率所影响。

为了获得稳定的统计,我们可以用-count参数来获得多次测试。

$ go test -bench=. -count=10 goos: linux goarch: amd64 BenchmarkFib20-8 50000 38395 ns/op BenchmarkFib20-8 50000 37019 ns/op BenchmarkFib20-8 50000 36974 ns/op BenchmarkFib20-8 50000 41050 ns/op BenchmarkFib20-8 50000 36131 ns/op BenchmarkFib20-8 30000 37317 ns/op BenchmarkFib20-8 30000 42872 ns/op BenchmarkFib20-8 30000 41711 ns/op BenchmarkFib20-8 50000 36517 ns/op BenchmarkFib20-8 50000 40240 ns/op PASS ok _/home/shikanon/study/fib 20.996s

接下来可以利用benchstat包计算平均值,benchstat是官方pref项目中的命令行工具,可以用来做各种性能测试的分析,比如计算多次测试的平均值核方差:

$benchstat old.txt name time/op Fib20-8 39.4µs ±13%

ps:由于prefgolang/x中的包,我们需要git clone下载下来,然后到benchstat目录下做编译go build -o $GOROOT/bin/benchstat main.go

也可以用来计算两种不同方法进行比较,看他们的p值是否显著。在对比两个或以上的用例时可以用go test -c生成二进制测试文件。

./fib2.test -bench=. -test.count=10 > old.txt ./fib.test -bench=. -test.count=10 > new.txt $ benchstat old.txt new.txt name old time/op new time/op delta Fib20-8 44.3µs ± 6% 25.6µs ± 2% -42.31% (p=0.000 n=10+10)

关于 golang 的 for range 的性能比较

在潜意识中,我们通常会觉得遍历一个数组会更慢,但由于for range遍历的时候不需要对对下标越界的判断,因此性能会比遍历判断更好些。

代码语言:javascript
复制
package main

import (
	"testing"
)

func BenchmarkArray(b *testing.B) {
	var a = 0
	for i := 0; i < b.N; i++ {
		var times [100][0]int
		for range times {
			a += 1
		}
	}
}

func BenchmarkInt(b *testing.B) {
	var a = 0
	for i := 0; i < b.N; i++ {
		for i := 0; i < 100; i++ {
			a += 1
		}
	}
}

(注:[100][0]int类型的数组由于元素[0]int的内存为0,即使第一维数组有长度也不需要付出额外的代价)

测试结果:

go test -bench=. goos: windows goarch: amd64 BenchmarkArray-8 50000000 36.1 ns/op BenchmarkInt-8 20000000 65.9 ns/op PASS

我们可以看出 for range一个空数组比for要快一倍。

切片高效删除指定元素操作

利用了零长切片的特性,实现高效删除技巧:

代码语言:javascript
复制
func TrimSpaceWithAssignment(s []byte) []byte {
	var b = make([]byte, len(s))
	for i, x := range s {
		if x != ' ' {
			b[i] = x
		}
	}
	return b
}

func TrimSpaceWithNil(s []byte) []byte {
	var b = make([]byte, 0, len(s))
	for _, x := range s {
		if x != ' ' {
			b = append(b, x)
		}
	}
	return b
}

func TrimSpaceWithOrigin(s []byte) []byte {
	b := s[:0]
	for _, x := range s {
		if x != ' ' {
			b = append(b, x)
		}
	}
	return b
}

func BenchmarkAssignment(b *testing.B) {
	s := []byte("asdf asdf sa fd dsa f d")
	for i := 0; i < b.N; i++ {
		TrimSpaceWithAssignment(s)
	}
}

func BenchmarkNilSlice(b *testing.B) {
	s := []byte("asdf asdf sa fd dsa f d")
	for i := 0; i < b.N; i++ {
		TrimSpaceWithNil(s)
	}
}

func BenchmarkOrignSlice(b *testing.B) {
	s := []byte("asdf asdf sa fd dsa f d")
	for i := 0; i < b.N; i++ {
		TrimSpaceWithOrigin(s)
	}
}

测试结果:

go test -bench=. goos: windows goarch: amd64 BenchmarkAssignment-8 30000000 43.7 ns/op BenchmarkNilSlice-8 30000000 47.6 ns/op BenchmarkOrignSlice-8 50000000 22.7 ns/op

参考

https://dave.cheney.net/high-performance-go-workshop/dotgo-paris.html https://github.com/chai2010/advanced-go-programming-book/blob/master/ch1-basic/ch1-03-array-string-and-slice.md

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2019-03-30,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 关于 golang 的 for range 的性能比较
  • 切片高效删除指定元素操作
  • 参考
相关产品与服务
命令行工具
腾讯云命令行工具 TCCLI 是管理腾讯云资源的统一工具。使用腾讯云命令行工具,您可以快速调用腾讯云 API 来管理您的腾讯云资源。此外,您还可以基于腾讯云的命令行工具来做自动化和脚本处理,以更多样的方式进行组合和重用。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档