go 7年前

Go语言学习笔记(1)--- 基准测试

作者头像 刘宇帅
2678 0

什么是基准测试

基准测试是测量一个程序在固定工作负载下的性能。我们通常会用来对比对同一个问题不同解决方案的性能,从而帮助我们做决定选择哪个方案更合理。

Go 的基准测试

Go 基准测试规则如下:

  • 必须放在以 _test.go 结尾的文件中。
  • 函数名必须以 Benchmark 开头
  • 函数必须接受一个参数 *testing.B
  • 没有返回值

基准测试受机器状态等因素的影响每次跑的结果很难保持完全一致,但是基本会在一个很小的范围内波动。
写一个简单的基准测试如下

import "testing"

func sum() int {
    sum := 0
    for i := 1; i <= 100; i++ {
        sum += i
    }
    return sum
}

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

运行结果如下

> $ go test -bench=Sum -run=^1
goos: darwin
goarch: amd64
pkg: github.com/yushuailiu/easyGolang/benchmark
BenchmarkSum-4      20000000            60.8 ns/op
PASS
ok      github.com/yushuailiu/easyGolang/benchmark  1.287s

跑基准测试仍然使用 go test 命令,但是需要添加 -bench= 参数来标识跑基准测试,-bench 参数值跟一个正则表达式表示需要跑的基准测试,我们可以用 . 表示跑所有的,如果我们基准测试比较多可以使用正则指明,比如上面的我们只需要跑 Sum。-run=^1 是为了禁止跑单元测试,因为 go test 默认会在跑基准测试前跑单元测试,我们这里为了单纯的跑基准测试,所以需要禁止单元测试,而 -run=^1 用来指明需要跑的单元测试的正则,很显然没有以 1 开头的的单元测试,所有该参数的意思是禁止跑单元测试。
输出结果我们可以看到 BenchmarkSum-4,这里是 4 是指运行时的 GOMAXPROC 的值。 20000000 表示 t.N 的值,即我们跑了 20000000 次 sum(),60.8 表示跑一次需要 60.8 纳秒。
基准测试参数-benchtime= 用来指定基准测试的时间,默认基准测试是跑1s,有时候性能相当的两个程序可以通过增加压测时间对比差别,但是最长不要超过3s,因为如果算法是稳定的时间再长是没有意义的。
基准测试参数-benchmem用来显示每次操作内存分配的次数,以及每次操作分配的字节数,可以用来对比不同程序的性能根源,因为很显然内存操作多,分配字节多的自然会需要更多地时间。

基准测试的具体使用

我前面有写了一篇关于冒泡排序算法的实现,里面有提到 3 种实现,我们这里对 3 种实现做基准测试比较 3 种实现的性能。基准测试代码源码地址 算法地址

import "testing"

var list = []map[string][]int{
    {
        "origin": {10, 9, 8, 7, 6, 5, 4, 3, 2, 1},
        "result": {1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
    },
    {
        "origin": {22, 33, 22, 1, 2, 11, 22, 123, 110},
        "result": {1, 2, 11, 22, 22, 22, 33, 110, 123},
    },
    {
        "origin": {22, 23, 10, 9, 54, 17, 100, 2},
        "result": {2, 9, 10, 17, 22, 23, 54, 100},
    },
    {
        "origin": {22, 23, 22, 23, 22, 23, 22, 23},
        "result": {22, 22, 22, 22, 23, 23, 23, 23},
    },
    {
        "origin": {1, 1111, 298989, 8422, 2222, 44422},
        "result": {1, 1111, 2222, 8422, 44422, 298989},
    },
    {
        "origin": {22, 10, 1, 99, 22, 10, 11, 10, 2, 100},
        "result": {1, 2, 10, 10, 10, 11, 22, 22, 99, 100},
    },
    {
        "origin": {23, 22},
        "result": {22, 23},
    },
    {
        "origin": {1},
        "result": {1},
    },
    {
        "origin": {},
        "result": {},
    },
}

func listEqual(list1, list2 []int) bool {
    for index, _ := range list1 {
        if list1[index] != list2[index] {
            return false
        }
    }
    return true
}

func BenchmarkBubbleSort1(b *testing.B) {
    for i := 0; i < b.N; i++ {
        for _, item := range list {
            origin := make([]int, len(item["origin"]))
            copy(origin, item["origin"])
            BubbleSort1(origin)
            if !listEqual(origin, item["result"]) {
                b.Error("bubble sort", item["origin"], "should be", item["result"],
                    "but get", origin)
            }
        }
    }
}
func BenchmarkBubbleSort2(b *testing.B) {
    for i := 0; i < b.N; i++ {
        for _, item := range list {
            origin := make([]int, len(item["origin"]))
            copy(origin, item["origin"])
            BubbleSort2(origin)
            if !listEqual(origin, item["result"]) {
                b.Error("bubble sort", item["origin"], "should be", item["result"],
                    "but get", origin)
            }
        }
    }
}
func BenchmarkBubbleSort3(b *testing.B) {
    for i := 0; i < b.N; i++ {
        for _, item := range list {
            origin := make([]int, len(item["origin"]))
            copy(origin, item["origin"])
            BubbleSort3(origin)
            if !listEqual(origin, item["result"]) {
                b.Error("bubble sort", item["origin"], "should be", item["result"],
                    "but get", origin)
            }
        }
    }
}

运行基准测试

> $ go test -bench=BubbleSort* -run=^1 -v
goos: darwin
goarch: amd64
pkg: github.com/yushuailiu/go-algorithm/sort
BenchmarkBubbleSort1-4       1000000          1012 ns/op
BenchmarkBubbleSort2-4       2000000           993 ns/op
BenchmarkBubbleSort3-4       2000000           883 ns/op
PASS
ok      github.com/yushuailiu/go-algorithm/sort 6.676s

可以看到 BubbleSort2 比 BubbleSort1 稍有提升,而 BubbleSort3 比 BubbleSort2 提升还是挺大的。

作者头像

刘宇帅

非著名程序员,全栈开发工程师,长期专注系统开发与架构设计。

提示

功能待开通!


暂无评论~

相关文章

震惊!同事小张踩了 gorm 神奇的 Scan 函数的坑

gorm 简介 gorm 是 go 语言中实现的比较好的 ORM 包,且是国人开发的。项目地址 事故描述 Scan 是 gorm 提供的一个把数据库结果读取到 struct 的函数。定义如下: // Scan scan value to a struct func (s *DB) Scan(dest interface{}) *DB { return s.NewScope(s.Value).Set("gorm:query_destination", dest).callCallbacks(s.parent.callbacks.queries).db } 今天同事小张写代码的时候写了一个

goland集成fmt goimports gometalinter

三个工具介绍 go fmt是用来规范go文件格式,比如格式化单个文件 go fmt xxx.go goimports 用来检查导入包,导入依赖包,删除不依赖的包 gometalinter 集成go语言几乎所有检测工具,静态分析代码,包含功能如下 go vet -工具可以帮我们静态分析我们的源码存在的各种问题,例如多余的代码,提前return的逻辑,struct的tag是否符合标准等。 go tool vet --shadow -用来检查作用域里面设置的局部变量名和全局变量名设置一样导致全局变量设置无效的问题 gotype -类型检测用来检测传递过来的变量和预期变量类型一致 gotype -x

Golang 本地编译和交叉编译

Go 语言的可移植性 Java 平台可移植性是众所都知的,Java 的可移植性依赖于其虚拟机 JVM,Java 实现了对不同平台的 JVM 的支持,那么一份 Java 代码就可以在各个平台上运行。而 Go 语言的可移植性也是依赖于其 runtime,runtime 去对接操作系统层,用户代码在 runtime 中运行,用户代码就不用去关心平台问题。 查看 Go 支持的OS和平台: &gt; $ go version go version go1.11 darwin/amd64 liushuai@liushuaideMacBook-Pro  ~/Documents/goProject/src

[转]Golang中JSON使用小技巧

临时忽略掉struct中空字段 type User struct { Email string `json:"email"` Password string `json:"password"` } 当我们把用户信息返回给前端的时候显然需要忽略调Password 字段,则可以这样做: json.Marshal(struct{ *User Password bool `json:"password,omitempty"` }{ User:user, }) 临时添加额外字段 type User struct { Email string `json:"

golang 中丰富的字符串格式化

golang中字符串格式化输出 package main import ( "fmt" "os" ) type point struct { x, y int } func main() { // Go提供了几种打印格式,用来格式化一般的Go值,例如 // 下面的%v打印了一个point结构体的对象的值 p := point{1, 2} fmt.Printf("%v\n", p) // 如果所格式化的值是一个结构体对象,那么`%+v`的格式化输出 // 将包括结构体的成员名称和值 fmt.Printf("%