zoukankan      html  css  js  c++  java
  • Golang基准测试

    1、基本使用

    基准测试常用于代码性能测试,函数需要导入testing包,并定义以Benchmark开头的函数, 参数为testing.B指针类型,在测试函数中循环调用函数多次

    go test testcalc/calc -bench .
    go test testcalc/calc -bench . -run=none
    # 显示内存信息
    go test testcalc/calc -bench . -benchmem
    go test -bench=. -benchmem -run=none
    

    go test会在运行基准测试之前之前执行包里所有的单元测试,所有如果你的包里有很多单元测试,或者它们会运行很长时间,你也可以通过go test-run标识排除这些单元测试

    业务代码fib.go,测试斐波那契数列

    package pkg06
    
    func fib(n int) int {
    	if n == 0 || n == 1 {
    		return n
    	}
    	return fib(n-2) + fib(n-1)
    }
    

    测试代码fib_test.go

    package pkg06
    
    import "testing"
    
    func BenchmarkFib(b *testing.B) {
    	for n := 0; n < b.N; n++ {
    		fib(30)
    	}
    }
    

    执行测试

    ➜  go test -bench=. -run=none
    goos: darwin
    goarch: amd64
    pkg: pkg06
    cpu: Intel(R) Core(TM) i7-8850H CPU @ 2.60GHz
    BenchmarkFib-12              250           4682682 ns/op
    PASS
    ok      pkg06   1.875s
    ➜  go test -bench=. -benchmem -run=none
    goos: darwin
    goarch: amd64
    pkg: pkg06
    cpu: Intel(R) Core(TM) i7-8850H CPU @ 2.60GHz
    BenchmarkFib-12              249           4686452 ns/op               0 B/op          0 allocs/op
    PASS
    ok      pkg06   1.854s
    

    2、bench的工作原理

    • 基准测试函数会被一直调用直到b.N无效,它是基准测试循环的次数
    • b.N1开始,如果基准测试函数在1秒内就完成 (默认值),则b.N增加,并再次运行基准测试函数
    • b.N的值会按照序列1,2,5,10,20,50,... 增加,同时再次运行基准测测试函数
    • 上述结果解读代表1秒内运行了250次,每次4682682 ns
    • -12后缀和用于运行次测试的GOMAXPROCS值有关。 与GOMAXPROCS一样,此数字默认为启动时Go进程可见的CPU数。 可以使用-cpu标识更改此值,可以传入多个值以列表形式来运行基准测试

    3、传入cpu num进行测试

    ➜  go test -bench=. -cpu=1,2,4  -benchmem -run=none
    goos: darwin
    goarch: amd64
    pkg: pkg06
    cpu: Intel(R) Core(TM) i7-8850H CPU @ 2.60GHz
    BenchmarkFib                 244           4694667 ns/op               0 B/op          0 allocs/op
    BenchmarkFib-2               255           4721201 ns/op               0 B/op          0 allocs/op
    BenchmarkFib-4               256           4756392 ns/op               0 B/op          0 allocs/op
    PASS
    ok      pkg06   5.826s
    

    4、count多次运行基准测试

    因为热缩放、内存局部性、后台处理、gc活动等等会导致单次的误差,所以一般会进行多次测试

    ➜  go test -bench=. -count=10  -benchmem -run=none 
    goos: darwin
    goarch: amd64
    pkg: pkg06
    cpu: Intel(R) Core(TM) i7-8850H CPU @ 2.60GHz
    BenchmarkFib-12              217           5993577 ns/op               0 B/op          0 allocs/op
    BenchmarkFib-12              246           5065577 ns/op               0 B/op          0 allocs/op
    BenchmarkFib-12              244           4955397 ns/op               0 B/op          0 allocs/op
    BenchmarkFib-12              255           4689529 ns/op               0 B/op          0 allocs/op
    BenchmarkFib-12              254           4879802 ns/op               0 B/op          0 allocs/op
    BenchmarkFib-12              254           4691213 ns/op               0 B/op          0 allocs/op
    BenchmarkFib-12              255           4772108 ns/op               0 B/op          0 allocs/op
    BenchmarkFib-12              240           4724141 ns/op               0 B/op          0 allocs/op
    BenchmarkFib-12              255           4717087 ns/op               0 B/op          0 allocs/op
    BenchmarkFib-12              255           4787803 ns/op               0 B/op          0 allocs/op
    PASS
    ok      pkg06   18.166s
    

    5、benchtime指定运行秒数

    有的函数比较慢,为了更精确的结果,可以通过-benchtime标志指定运行时间,从而使它运行更多次

    ➜  go test -bench=. -benchtime=5s  -benchmem -run=none
    goos: darwin
    goarch: amd64
    pkg: pkg06
    cpu: Intel(R) Core(TM) i7-8850H CPU @ 2.60GHz
    BenchmarkFib-12             1128           4716535 ns/op               0 B/op          0 allocs/op
    PASS
    ok      pkg06   7.199s
    

    6、ResetTimer重置定时器

    可能在真正测试之前还需要做很多例如初始化等工作,这时可以在需要测试的函数执行之初添加一个重置定时器的功能,这样最终得到的时间就更为精确

    package pkg06
    
    import (
    	"testing"
    	"time"
    )
    
    func BenchmarkFib(b *testing.B) {
    	time.Sleep(3 * time.Second)
    	b.ResetTimer()
    	for n := 0; n < b.N; n++ {
    		fib(30)
    	}
    }
    

    执行测试

    ➜  go test -bench=. -benchtime=5s  -benchmem -run=none
    goos: darwin
    goarch: amd64
    pkg: pkg06
    cpu: Intel(R) Core(TM) i7-8850H CPU @ 2.60GHz
    BenchmarkFib-12             1239           4712413 ns/op               0 B/op          0 allocs/op
    PASS
    ok      pkg06   16.122s
    

    7、benchmem展示内存消耗

    • 例如测试大cap的切片,直接用cap初始化和cap动态扩容进行对比
    package pkg07
    
    import (
    	"math/rand"
    	"testing"
    	"time"
    )
    
    // 指定大的cap的切片
    func generateWithCap(n int) []int {
    	rand.Seed(time.Now().UnixNano())
    	nums := make([]int, 0, n)
    	for i := 0; i < n; i++ {
    		nums = append(nums, rand.Int())
    	}
    	return nums
    }
    
    // 动态扩容的slice
    func generateDynamic(n int) []int {
    	rand.Seed(time.Now().UnixNano())
    	nums := make([]int, 0)
    	for i := 0; i < n; i++ {
    		nums = append(nums, rand.Int())
    	}
    	return nums
    }
    
    func BenchmarkGenerateWithCap(b *testing.B) {
    	for n := 0; n < b.N; n++ {
    		generateWithCap(100000)
    	}
    }
    
    func BenchmarkGenerateDynamic(b *testing.B) {
    	for n := 0; n < b.N; n++ {
    		generateDynamic(100000)
    	}
    }
    

    执行测试

    ➜  go test -bench=. -benchmem -run=none 
    goos: darwin
    goarch: amd64
    pkg: pkg07
    cpu: Intel(R) Core(TM) i7-8850H CPU @ 2.60GHz
    BenchmarkGenerateWithCap-12          672           1729465 ns/op          802817 B/op          1 allocs/op
    BenchmarkGenerateDynamic-12          561           2122992 ns/op         4654346 B/op         30 allocs/op
    PASS
    ok      pkg07   3.777s
    

    结论:用cap初始化好的性能可以高一个数据量级

    • 例如测试测试函数复杂度,不带capslice动态扩容

    对上面代码中调用动态扩容生成切片进行再次封装

    package pkg08
    
    import (
    	"math/rand"
    	"testing"
    	"time"
    )
    
    // 指定大的cap的切片
    func generateWithCap(n int) []int {
    	rand.Seed(time.Now().UnixNano())
    	nums := make([]int, 0, n)
    	for i := 0; i < n; i++ {
    		nums = append(nums, rand.Int())
    	}
    	return nums
    }
    
    // 动态扩容的slice
    func generateDynamic(n int) []int {
    	rand.Seed(time.Now().UnixNano())
    	nums := make([]int, 0)
    	for i := 0; i < n; i++ {
    		nums = append(nums, rand.Int())
    	}
    	return nums
    }
    
    func benchmarkGenerate(i int, b *testing.B) {
    	for n := 0; n < b.N; n++ {
    		generateDynamic(i)
    	}
    }
    
    func BenchmarkGenerateDynamic1000(b *testing.B)     { benchmarkGenerate(1000, b) }
    func BenchmarkGenerateDynamic10000(b *testing.B)    { benchmarkGenerate(10000, b) }
    func BenchmarkGenerateDynamic100000(b *testing.B)   { benchmarkGenerate(100000, b) }
    func BenchmarkGenerateDynamic1000000(b *testing.B)  { benchmarkGenerate(1000000, b) }
    func BenchmarkGenerateDynamic10000000(b *testing.B) { benchmarkGenerate(10000000, b) }
    

    执行测试

    ➜  go test -bench=. -benchmem -run=none 
    goos: darwin
    goarch: amd64
    pkg: pkg08
    cpu: Intel(R) Core(TM) i7-8850H CPU @ 2.60GHz
    BenchmarkGenerateDynamic1000-12            39540             26557 ns/op           16376 B/op         11 allocs/op
    BenchmarkGenerateDynamic10000-12            5452            210894 ns/op          386296 B/op         20 allocs/op
    BenchmarkGenerateDynamic100000-12            572           2106325 ns/op         4654341 B/op         30 allocs/op
    BenchmarkGenerateDynamic1000000-12            48          23070939 ns/op        45188416 B/op         40 allocs/op
    BenchmarkGenerateDynamic10000000-12            5         212567041 ns/op        423503110 B/op        50 allocs/op
    PASS
    ok      pkg08   9.686s
    

    结论:输入变为原来的10倍,单次耗时也差不多是上一级的10倍。说明这个函数的复杂度是接近线性的

    See you ~

  • 相关阅读:
    JavaScript prototype应用
    HTML表格
    ramnit病毒
    HTML简历表格
    Oracle、SQL Server、MySQL数据类型对比
    Oracle、SQL Server、MySQL分页方法
    java操作数据库出错
    SQL基本CRUD
    Oracle基础函数
    SQL多表查询
  • 原文地址:https://www.cnblogs.com/ssgeek/p/15159530.html
Copyright © 2011-2022 走看看