go 基准测试
2022-01-08 本文已影响0人
wayyyy
基准测试主要用于评估代码的性能,Go 语言测试框架可以让我们很容易地进行基准测试,同样需要遵循四点规则:
- 基准测试函数必须以 Benchmark 开头,函数的签名必须接收一个指向 testing.B 类型的指针,并且不能返回任何值。
func BenchmarkStringPlus100(b *testing.B) { // ... }
- 最后的 for 循环很重要,被测试的代码要放到循环里,b.N 是基准测试框架提供的,表示循环的次数,因为需要反复调用测试的代码,才可以评估性能。
for i := 0; i < b.N; i++ { stringBuilder(p) }
下面展示一个例子测试不同方法的字符串拼接效率。
- stringcat.go
package main import ( "bytes" "fmt" "strings" ) const BLOG = "http://www.baidu.org/" func initStrings(N int) []string { s := make([]string, N) for i := 0; i < N; i++ { s[i] = BLOG } return s } func initStringi(N int) []interface{} { s := make([]interface{}, N) for i := 0; i < N; i++ { s[i] = BLOG } return s } func stringPlus(p []string) string { var s string l := len(p) for i := 0; i < l; i++ { s += p[i] } return s } func stringFmt(p []interface{}) string { return fmt.Sprint(p...) } func stringJoin(p []string) string { return strings.Join(p, "") } func stringBuffer(p []string) string { var b bytes.Buffer l := len(p) for i := 0; i < l; i++ { b.WriteString(p[i]) } return b.String() } func stringBuilder(p []string) string { var b strings.Builder l := len(p) for i := 0; i < l; i++ { b.WriteString(p[i]) } return b.String() }
- stringcat_test.go
package main import "testing" func BenchmarkStringPlus100(b *testing.B) { p := initStrings(100) b.ResetTimer() for i := 0; i < b.N; i++ { stringPlus(p) } } func BenchmarkStringFmt100(b *testing.B) { p := initStringi(100) b.ResetTimer() for i := 0; i < b.N; i++ { stringFmt(p) } } func BenchmarkStringJoin100(b *testing.B) { p := initStrings(100) b.ResetTimer() for i := 0; i < b.N; i++ { stringJoin(p) } } func BenchmarkStringBuffer100(b *testing.B) { p := initStrings(100) b.ResetTimer() for i := 0; i < b.N; i++ { stringBuffer(p) } } func BenchmarkStringBuilder100(b *testing.B) { p := initStrings(100) b.ResetTimer() for i := 0; i < b.N; i++ { stringBuilder(p) } }
运行测试:
go test -bench=. -benchtime=3s .
goos: linux
goarch: amd64
pkg: ch18/main
BenchmarkStringPlus100 119738 30039 ns/op
BenchmarkStringFmt100 1000000 3178 ns/op
BenchmarkStringJoin100 2409663 1513 ns/op
BenchmarkStringBuffer100 1487332 2456 ns/op
BenchmarkStringBuilder100 1730882 2070 ns/op
PASS
ok ch18/main 24.062s
从上面基准测试可以得出:
- 表现好的还是Join和Builder。
- 这两个方法的使用侧重点有些不一样, 如果有现成的数组、切片那么可以直接使用Join,但是如果没有,并且追求灵活性拼接,还是选择Builder。 Join还是定位于有现成切片、数组的(毕竟拼接成数组也要时间),并且使用固定方式进行分解的,比如逗号、空格等,局限比较大。