2013-10-05 18 views
6

Używam jmh (http://openjdk.java.net/projects/code-tools/jmh/) do testowania niektórych metod. Mam również zestaw parametrów, które chcę użyć jako argumentów do uruchomienia tej metody. Czy jest możliwe wygenerowanie metody dla każdej poszczególnej wartości parametru (z adnotacją @GenerateMicroBenchmark)?Jak generować metody w benchmarkach jmh?

Teraz używam podobną realizację, ale to nie jest tak wygodne, ponieważ muszę napisać dużo jednolitego kodu ręcznie:

interface State { 
    int action(); 
    void prepare(); 
} 

class Method { 
    ...; 
    toString() { return "State1_" + param; } 
} 

{ 
    Method[] states; 
    curState = -1; 
    count = 0; 
    int[] params = {1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000, 10000, 11000, 12000}; 
    for (int param: params) { 
     states[count++] = new Method(param); 
    } 
} 

@Setup(Level.Invocation) 
public void prepareState() { 
    if (curState != -1) { 
    states[curState].prepare(); 
    } 
} 

@GenerateMicroBenchmark 
public int param_1000() { 
    curState = 0; 
    return states[curState].action(); 
} 

@GenerateMicroBenchmark 
public int param_2000() { 
    curState = 1; 
    return states[curState].action(); 
} 

@GenerateMicroBenchmark 
public int param_3000() { 
    curState = 2; 
    return states[curState].action(); 
} 
... 
@GenerateMicroBenchmark 
public int param_12000() { 
    curState = 11; 
    return states[curState].action(); 
} 

Odpowiedz

3

Benchmarki zwykle nie nadają się do ponownego wykorzystania bez poważnego pęknięcia. Większość prób, które widziałem, próbujących uprościć swoje benchmarki, złamało ich wierzenia. Na przykład użycie tablic może zrównoważyć wyniki dla nano-testów porównawczych (np. Jeśli action() jest małe). Level.Invocation jest zwykle złym pomysłem, jak podano w its Javadoc.

Najważniejsze jest to, że pewne użycie API jest dozwolone, ale niekoniecznie oznacza, że ​​należy z niego korzystać. Oto co należy zrobić, zamiast:

@State(Scope.Thread) 
class MyBenchmark { 
    Method[] states; 

    @Setup 
    public void setup() { 
     int[] params = {1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000, 10000, 11000, 12000}; 
     int count = 0; 
     for (int param: params) { 
      states[count++] = new Method(param); 
     } 
    } 

    int doWork(int idx) { 
     states[idx].prepare(); 
     return states[idx].action(); 
    } 

    @GenerateMicroBenchmark 
    public int param_1000() { 
     doWork(0); 
    } 

    ... 

    @GenerateMicroBenchmark 
    public int param_12000() { 
     doWork(11); 
    } 
} 

... albo nawet:

@State(Scope.Thread) 
class MyBenchmark { 
    Method p1000, p2000, ..., p12000; 

    @Setup 
    public void setup() { 
     p1000 = new Method(p1000); 
     p2000 = new Method(p2000); 
     ... 
     p12000 = new Method(p12000); 
    } 

    @GenerateMicroBenchmark 
    public int param_1000() { 
     p1000.prepare(); 
     return p1000.action(); 
    } 

    ... 

    @GenerateMicroBenchmark 
    public int param_12000() { 
     p12000.prepare(); 
     return p12000.action(); 
    } 
} 

Alternatywą byłoby przyjmowanie parametru z zewnątrz, a przy użyciu Java API żonglować parametru. Dla przykładu:

@State(Scope.Thread) 
class MyBenchmark { 
    final int param = Integer.getInteger("param", 1000); 

    Method m; 

    @Setup 
    public void setup() { 
     m = new Method(param); 
    } 

    @GenerateMicroBenchmark 
    public int work() { 
     m.prepare(); 
     return m.action(); 
    } 

    public static void main(String[] args) throws RunnerException { 
     Options opts = new OptionsBuilder() 
       .include(".*") 
       .jvmArgs("-Dparam=2000") 
       .build(); 

     RunResult runResult = new Runner(opts).runSingle(); 
     Result result = runResult.getPrimaryResult(); 

     System.out.println(); 
     System.out.println("API replied benchmark score: " + result.getScore() + " " + result.getScoreUnit() + " over " + result.getStatistics().getN() + " iterations"); 
    } 
} 
Powiązane problemy