benchmark(go语言测试分析)

1 篇文章 0 订阅

benchmark测试(以字符串拼接为例)

1、首先实现拼接字符串拼接函数

// generate_test.go
package main

import (
    "math/rand"
    "testing"
    "time"
)

// 首先实现一个生成长度为 n 的随机字符串的函数
const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"

func randomString(n int) string {
	b := make([]byte, n)
	for i := b {
	b[i] = letterBytes[rand.Intn(len(letterBytes))]
	}
	return string(b)
}

// 使用+实现字符串拼接 拼接n次
func plusContact(n int, str string) string {
	s := ""
	for i := 0; i < n; i++ {
	s += str 
	}
	return s
}

// 使用fmt.Sprintf实现
func sprintfContact(n int, str string) string {
	s := "
	for i := 0; i < n; i++ {
		s = fmt.Sprintf("%s%s", s, str)
	}
	return s 
}

// 使用strings.Builder(非线程安全,StringBuilder 相较于 StringBuffer 有速度优势)
func builderConcat(n int, str string) string {
	var builder strings.Builder
	for i := 0; i < n; i++ {
		builder.WriteString(str)
	}
}

// 使用strings.Buffer
func bufferConcat(n int, s string) string {
	buf := new(bytes.Buffer)
	for i := 0; i < n; i++ {
		buf.WriteString(s)
	}
	return buf.String()
}

// 使用bytes.Buffer
func bufferConcat(n int, s string) string {
	buf := new(bytes.Buffer)
	for i := 0; i < n; i++ {
		buf.WriteString(s)
	}
}

// 使用[]byte
func byteConcat(n int, str string) string {
	for i := 0; i < n; i++ {
		buf = append(buf, str...)
	}
}

// 分配容量的[]byte
unc preByteConcat(n int, str string) string {
	buf := make([]byte, 0, n*len(str))
	for i := 0; i < n; i++ {
		buf = append(buf, str...)
	}
	return string(buf)
}

// 直接在这写过benchmark测试文件吧
// 生成长度为10的字符串,拼接1w次
func benchmark(b *testing.B, f func(int, string) string) {
	var str = randomString(10)
	for i := 0; i < b.N; i++ {
		f(10000, str)
	}
}

func BenchmarkPlusConcat(b *testing.B)    { benchmark(b, plusConcat) }
func BenchmarkSprintfConcat(b *testing.B) { benchmark(b, sprintfConcat) }
func BenchmarkBuilderConcat(b *testing.B) { benchmark(b, builderConcat) }
func BenchmarkBufferConcat(b *testing.B)  { benchmark(b, bufferConcat) }
func BenchmarkByteConcat(b *testing.B)    { benchmark(b, byteConcat) }
func BenchmarkPreByteConcat(b *testing.B) { benchmark(b, preByteConcat) }

测试该用例

$ go test -bench="Concat$" -benchmem .
goos: darwin
goarch: amd64
pkg: example
BenchmarkPlusConcat-8         19      56 ms/op   530 MB/op   10026 allocs/op
BenchmarkSprintfConcat-8      10     112 ms/op   835 MB/op   37435 allocs/op
BenchmarkBuilderConcat-8    8901    0.13 ms/op   0.5 MB/op      23 allocs/op
BenchmarkBufferConcat-8     8130    0.14 ms/op   0.4 MB/op      13 allocs/op
BenchmarkByteConcat-8       8984    0.12 ms/op   0.6 MB/op      24 allocs/op
BenchmarkPreByteConcat-8   17379    0.07 ms/op   0.2 MB/op       2 allocs/op
PASS
ok      example 8.627s

参数说明:
1、属性 b.N 表示这个用例需要运行的次数

2、BenchmarkFib-8 中的 -8 即 GOMAXPROCS,默认等于 CPU 核数。可以通过 -cpu 参数改变 GOMAXPROCS,-cpu 支持传入一个列表作为参数,例如:

$ go test -bench='Fib$' -cpu=2,4 .

3、第二列参数指的是用例执行的次数 ,第三列指的是没次花费时间,第四列指的是分配多少内存,第五列指的是分配了几次内存

//------------cpu默认8核----执行次数--每次花费时间---内存分配--------分配了几次
BenchmarkPlusConcat-8         19      56 ms/op   530 MB/op   10026 allocs/op

由上面的结论可知

  • 使用 + 和 fmt.Sprintf 的效率是最低的,和其余的方式相比,性能相差约 1000 倍,而且消耗了超过 1000 倍的内存。当然
    fmt.Sprintf 通常是用来格式化字符串的,一般不会用来拼接字符串。
  • strings.Builder、bytes.Buffer 和 []byte
    的性能差距不大,而且消耗的内存也十分接近,性能最好且消耗内存最小的是
    preByteConcat,这种方式预分配了内存,在字符串拼接的过程中,不需要进行字符串的拷贝,也不需要分配新的内存,因此性能最好,且内存消耗最小。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值