【问题标题】:Is it possible to dynamically run benchmark tests?是否可以动态运行基准测试?
【发布时间】:2016-02-05 20:58:43
【问题描述】:

我有几个不同的接口实现,以及我想测试它们的各种因素。最终目标是为不同情况下的不同实现创建一个结果网格。

我可以为每个可能的组合编写一个测试,但这会让人筋疲力尽:

func Benchmark_ImplA_N100_X300(b *testing.B){
   impl := newImplA(100,300)
   runBenchmark(b,impl)
}

我添加的组合越多,我就越需要复制/粘贴。这很快就会变得很麻烦。

我想做这样的事情:

tests := []testing.InternalBenchmark{}
for _, n := range []int{50,100,500,10000}{
   for _,x := range []int{300,200}{
      for name, gen := range implementations{
         impl = gen(n,x)
         bm := testing.InternalBenchmark{Name: fmt.Sprint(name,x,n)}
         bm.F = func(b *testing.B){
            runBench(b,impl)
         }
         tests = append(tests,bm)
      }
   }
}
testing.RunBenchmarks(anything, tests)

这样我就可以更新组合列表,并且基准测试将神奇地在所有组合中运行。我在 main 和 TestMain 中尝试过类似的东西,但没有任何输出。我不确定是我用错了,还是测试包只是在做一些有趣的事情。

我真的不在乎 go test 工具是否可以处理它或者是否有其他方法。

【问题讨论】:

  • 一个简单的带有 go:generate 的 shell 脚本也可以工作

标签: testing go benchmarking


【解决方案1】:

是的,这是可能的。在您的测试文件 (xxx_test.go) 中创建您自己的 TestMain() 函数,并在其中组装动态基准案例(结构 testing.InternalBenchmark 的值)后,调用 testing.Main() 正确解析命令行标志,创建和设置testing.M,并准备并致电testing.RunBenchmarks()。这样,go test 仍然可以运行您的动态基准测试。

注意:testing.Main() 永远不会返回,因为它调用了os.Exit()。如果你想对基准结果进行进一步的记录和计算,你也可以调用testing.MainStart().Run()(这是testing.Main()所做的),你可以将M.Run()返回的退出代码传递给@ 987654341@.

下面是一个完整的测试文件,您可以使用go test -bench . 简单地运行它。

输出为:动态生成测试的基准测试结果(不同实现不同参数):

testing: warning: no tests to run
PASS
main.EngineA[impl=0, n=50, x=300]-4       100000             16716 ns/op
main.EngineB[impl=1, n=50, x=300]-4       100000             24788 ns/op
main.EngineA[impl=0, n=50, x=200]-4       100000             10764 ns/op
main.EngineB[impl=1, n=50, x=200]-4       100000             16415 ns/op
main.EngineA[impl=0, n=100, x=300]-4       50000             33426 ns/op
main.EngineB[impl=1, n=100, x=300]-4       30000             48466 ns/op
main.EngineA[impl=0, n=100, x=200]-4       50000             20452 ns/op
main.EngineB[impl=1, n=100, x=200]-4       50000             33134 ns/op
main.EngineA[impl=0, n=500, x=300]-4       10000            163087 ns/op
main.EngineB[impl=1, n=500, x=300]-4        5000            238043 ns/op
main.EngineA[impl=0, n=500, x=200]-4       10000            102662 ns/op
main.EngineB[impl=1, n=500, x=200]-4       10000            163113 ns/op
main.EngineA[impl=0, n=1000, x=300]-4       5000            319744 ns/op
main.EngineB[impl=1, n=1000, x=300]-4       3000            512077 ns/op
main.EngineA[impl=0, n=1000, x=200]-4      10000            201036 ns/op
main.EngineB[impl=1, n=1000, x=200]-4       5000            325714 ns/op
ok      _/xxx/src/play  27.307s

以及来源(一个测试文件,例如dynbench_test.go):

package main

import (
    "fmt"
    "testing"
)

type Engine interface {
    Calc()
}

type EngineA struct{ n, x int }

func (e EngineA) Calc() {
    for i := 0; i < e.n; i++ {
        a, b := make([]byte, e.x), make([]byte, e.x)
        copy(b, a)
    }
}

type EngineB struct{ n, x int }

func (e EngineB) Calc() {
    for i := 0; i < e.n*2; i++ {
        a, b := make([]byte, e.x/2), make([]byte, e.x/2)
        copy(b, a)
    }
}

func TestMain(m *testing.M) {
    implementations := [](func(n, x int) Engine){
        func(n, x int) Engine { return EngineA{n, x} },
        func(n, x int) Engine { return EngineB{n, x} },
    }

    benchmarks := []testing.InternalBenchmark{}
    for _, n := range []int{50, 100, 500, 1000} {
        for _, x := range []int{300, 200} {
            for name, gen := range implementations {
                impl := gen(n, x)
                bm := testing.InternalBenchmark{
                    Name: fmt.Sprintf("%T[impl=%d, n=%d, x=%d]", impl, name, n, x)}
                bm.F = func(b *testing.B) {
                    for i := 0; i < b.N; i++ {
                        impl.Calc()
                    }
                }
                benchmarks = append(benchmarks, bm)
            }
        }
    }
    anything := func(pat, str string) (bool, error) { return true, nil }

    testing.Main(anything, nil, benchmarks, nil)
}

注意事项 #2:

testing.Main()testing.MainStart()testing.InternalBenchmark 可能会在 Go 的未来版本中更改(或被删除):

一个内部函数/内部类型但是因为它是跨包而被导出的; “go test”命令的实现的一部分或被调用。

【讨论】:

  • 我还建议使用testing.B.Run("name", ...) 而不是testing.InternalBenchmark
【解决方案2】:

我想阅读文档会有所帮助。我忽略了

func Benchmark(f func(b *B)) BenchmarkResult

它将运行我的函数,而根本不需要任何测试工具。

Benchmark 对单个函数进行基准测试。用于创建自定义 不使用“go test”命令的基准测试。

这样我可以遍历我的测试用例并为每种可能性创建一个函数,然后直接运行基准测试。

【讨论】:

    猜你喜欢
    • 2016-08-16
    • 2010-09-29
    • 1970-01-01
    • 1970-01-01
    • 2015-05-04
    • 1970-01-01
    • 2015-06-10
    • 2018-04-22
    • 2016-01-03
    相关资源
    最近更新 更多