2015-11-05 10 views
6

Ho alcune diverse implementazioni di un'interfaccia e una varietà di fattori che voglio testarli sotto. L'obiettivo finale è quello di creare una griglia di risultati per diverse implementazioni in diverse situazioni.È possibile eseguire dinamicamente test di benchmark?

potrei scrivere un test per ogni possibile combinazione, ma che diventa faticoso:

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

I più combinazioni aggiungo, più devo copia/incolla. Questo diventa ingombrante velocemente.

mi piacerebbe fare qualcosa di simile:

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) 

in modo che io possa aggiornare la lista (s) di combinazioni, e parametri di riferimento magicamente funzionare in tutte le combinazioni. Ho provato qualcosa di simile in Main e in TestMain, e niente è mai uscito. Non sono sicuro se lo sto usando male, o se il pacchetto di test sta solo facendo qualcosa di divertente.

Non mi interessa davvero se lo strumento go test può gestirlo o se esiste un altro modo.

+0

uno script di shell semplice con go: generare potrebbe funzionare anche – kostya

risposta

5

Sì, è possibile. Nel file di test (xxx_test.go) creare la propria funzione TestMain() e al suo interno dopo aver assemblato i casi di benchmark dinamico (valori della struttura testing.InternalBenchmark), chiamare testing.Main() che analizza correttamente i flag della riga di comando, crea e imposta testing.M e prepara e chiama testing.RunBenchmarks(). In questo modo i benchmark dinamici saranno ancora eseguibili da go test.

Note: testing.Main() non restituirà mai come chiama os.Exit(). Se si desidera eseguire ulteriori registrazioni e calcoli sui risultati del benchmark, è anche possibile chiamare testing.MainStart().Run() (che è cosa fa testing.Main()) e si può passare il codice di uscita restituito da M.Run() a os.Exit().

Di seguito viene fornito un file di prova completo che è possibile eseguire semplicemente con go test -bench ..

Il risultato è: risultati benchmark di test generati dinamicamente (su diverse implementazioni con differenti parametri):

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 

E la fonte (un file di prova, ad esempio 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) 
} 

Note n. 2:

testing.Main(), testing.MainStart() e testing.InternalBenchmark può cambiare (o essere rimosso) in una versione futura di Go:

Una funzione interna/tipo interno ma esportata perché è un pacchetto incrociato; parte o chiamata dall'implementazione del comando "go test".

2

Suppongo che leggere i documenti aiuterebbe.Ho trascurato

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

che si svolgerà la mia funzione wothout necessità di test harness a tutti.

Benchmark benchmark una singola funzione. Utile per creare benchmark personalizzati che non utilizzano il comando "go test".

In questo modo posso scorrere i miei casi di test e creare una funzione per ogni possibilità, quindi eseguire direttamente il benchmark.