Go的testing包支持两种类型的测试,一种是用于检验程序功能性的功能测试(functional testing),而另一种则是用于查明任务单元性能的基准测试(benchmarking)。在上一节学习过如何进行功能测试之后,这一节我们将要学习如何进行基准测试。

跟单元测试一样,基准测试用例也需要放置到以_test.go为后缀的文件中,并且每个基准测试函数都需要符合以下格式:

func BenchmarkXxx(*testing.B) { ... }

作为例子,代码清单8-5展示了一个基准测试用例函数,这个函数定义在文件bench_test.go里面。

代码清单8-5 基准测试

package main

import (  
    "testing"
)

// benchmarking the decode function
func BenchmarkDecode(b *testing.B) {  
    for i := 0; i < b.N; i++ {//循环执行解码函数,以便对其进行b.N次基准测试    
        decode("post.json")  
    }
}

正如代码所示,在Go语言中进行基准测试是非常直观的:测试程序要做的就是将被测试的代码执行b.N次,以便准确地检测出代码的响应时间,其中b.N的值将根据被执行的代码而改变。比如,在上面展示的基准测试例子中,测试程序就将decode函数执行了b.N次。

为了运行基准测试用例,用户需要在执行go test命令时使用基准测试标志-bench,并将一个正则表达式用作该标志的参数,从而标识出自己想要运行的基准测试文件。当我们需要运行目录下的所有基准测试文件时,只需要把点.用作-bench标志的参数即可:

go test -v -cover -short –bench .

下面是这条命令的执行结果:

=== RUN TestDecode
--- PASS: TestDecode (0.00s)
=== RUN TestEncode
--- SKIP: TestEncode (0.00s)
main_test.go:38: Skipping encoding for now
=== RUN TestLongRunningTest
--- SKIP: TestLongRunningTest (0.00s)
main_test.go:44: Skipping long-running test in short mode
PASS
BenchmarkDecode  100000     19480 ns/op
coverage: 42.4% of statements
ok    unit_testing  2.243s

结果中的100000为测试时b.N的实际值,也就是函数被循环执行的次数。在这个例子中,迭代进行了10万次,并且每次耗费了19480 ns,即0.01948 ms。需要注意的是,在进行基准测试时,测试用例的迭代次数是由Go自行决定的,虽然用户可以通过限制基准测试的运行时间达到限制迭代次数的目的,但用户是无法直接指定迭代次数的——测试程序将进行足够多次的迭代,直到获得一个准确的测量值为止。在Go 1.5中,test子命令拥有一个-test.count标志,它可以让用户指定每个测试以及基准测试的运行次数,该标志的默认值为1

注意,上面的命令既运行了基准测试,也运行了功能测试。如果需要,用户也可以通过运行标志-run来忽略功能测试。-run标志用于指定需要被执行的功能测试用例,如果用户把一个不存在的功能测试名字用作-run标志的参数,那么所有功能测试都将被忽略。比如,如果我们执行以下命令:

go test -run x -bench .

那么由于我们的测试中不存在任何名字为x的功能测试用例,因此所有功能测试都不会被运行。在只执行基准测试的情况下,go test命令将产生以下结果:

PASS
BenchmarkDecode    100000       19714 ns/op
ok    unit_testing  2.150s

虽然检测单个函数的运行速度非常有用,但如果我们能够对比两个函数的运行速度,那么事情无疑会变得更加有意义!回想一下,我们在第7章曾经学过如何用两种不同的方法把JSON数据解封为结构:一种是使用Decode函数,另一种则是使用Unmarshal函数。因为上面的基准测试已经检测出了Decode函数的运行速度,那么接下来就让我们检测一下Unmarshal函数的运行速度吧。但是在进行基准测试之前,我们需要像代码清单8-6展示的那样,将解封操作的代码重构到main.go文件的unmarshal函数中。

代码清单8-6 解封JSON数据的函数

func unmarshal(filename string) (post Post, err error) {  
    jsonFile, err := os.Open(filename)  
    if err != nil {    
        fmt.Println("Error opening JSON file:", err)    
        return  
    }
    defer jsonFile.Close()  

    jsonData, err := ioutil.ReadAll(jsonFile)  
    if err != nil {    
        fmt.Println("Error reading JSON data:", err)    
        return  
    }  
    json.Unmarshal(jsonData, &post)  
    return
}

之后,我们还需要在基准测试文件bench_test.go中添加代码清单8-7所示的基准测试用例,以便对unmarshal函数进行基准测试。

代码清单8-7 对unmarshal函数进行基准测试

func BenchmarkUnmarshal(b *testing.B) {  
    for i := 0; i < b.N; i++ {    
        unmarshal("post.json")  
    }
}

一切准备就绪之后,再次运行基准测试命令,我们将得到以下结果:

PASS
BenchmarkDecode    100000       19577 ns/op
BenchmarkUnmarshal     50000       24532 ns/op
ok    unit_testing  3.628s

从上述结果可以看到,Decode函数每次执行需要耗费0.019577 ms,而Unmarshal函数每次执行需要耗费0.024532 ms,这说明Unmarshal函数比Decode函数慢了大约25%。

results matching ""

    No results matching ""