2017-09-04 9 views
0

Je suis en train d'implémenter un certain nombre d'algorithmes de tri en Java, et j'aimerais pouvoir chronométrer et comparer chaque algorithme. À l'heure actuelle, j'ai un script que chaque fois algorithme séparément, avec une énorme quantité de redondance de code:Passer des méthodes à un programme de test en Java

long min = 1000000; 
long startTime; 
long endTime; 

QuickSort quicksorter = new QuickSort(); 

for (int i = 0; i != 10000; ++i) { 
    startTime = System.nanoTime(); 
    quicksorter.sort(); 
    endTime = System.nanoTime(); 
    if ((endTime - startTime) < min) 
     min = endTime - startTime; 
} 

System.out.printf("Quicksort min time is %d ns.\n", min); 


BinaryInsertionSort binarysorter = new BinaryInsertionSort(); 
min = 1000000; 

for (int i = 0; i != 10000; ++i) { 
    startTime = System.nanoTime(); 
    binarysorter.sort(); 
    endTime = System.nanoTime(); 
    if ((endTime - startTime) < min) 
     min = endTime - startTime; 
} 

System.out.printf("Binary insertion sort min time is %d ns.\n", min); 

Avec six ou sept algorithmes de tri différents, cela commence à se sentir extrêmement inefficace. La solution évidente « python-esque » serait de définir une méthode qui prend des méthodes comme paramètres et les temps eux comme ceci:

long timeit(function func) { 
    min = 1000000; 

    for (int i = 0; i != 10000; ++i) { 
     startTime = System.nanoTime(); 
     func(); 
     endTime = System.nanoTime(); 
     if ((endTime - startTime) < min) 
      min = endTime - startTime; 
    } 

    System.out.printf("Min execution time is %d ns.\n", min); 
} 

Cependant, je crois qu'il est à déconseiller (et pratique) pour passer des méthodes comme paramètres Java, et le faire génériquement pourrait être impossible. Je suppose que chaque classe de tri peut hériter d'une classe abstraite qui implémente une méthode de benchmarking, mais y a-t-il une meilleure façon d'y parvenir en Java?

+0

Il suffit d'utiliser JMH: http://tutorials.jenkov.com/java-performance/jmh.html#your-first-jmh-benchmark – millimoose

Répondre

2

Cependant, je crois qu'il est à déconseiller (et peu pratique) pour passer des méthodes comme paramètres en Java, et ce, peut génériquement impossible

Hein?

Depuis Java 8, vous pouvez passer des méthodes génériques comme arguments; et ce que vous voulez ici est un Runnable.

Pourvu que vous avez deux méthodes dans une même classe:

public final class Foo 
{ 
    void f1() { /* whatever */ } 
    void f2() { /* whatever */ } 
} 

alors vous pouvez de temps à la fois dans le code en utilisant des références de méthode, comme dans:

final Runnable r1 = Foo::f1; 
final Runnable r2 = Foo::f2; 

et ensuite utiliser ces références de méthode (qui sont valides lambdas) dans un certain code de synchronisation:

long start, end; 

Stream.of(r1, r2).forEach(r -> { 
    start = System.nanoTime(); 
    r.run(); 
    end = System.nanoTime(); 
    System.out.println("Time spent in ns: " + (end - start)); 
}); 

Ensuite, vous pouvez affiner davantage votre code afin d'utiliser, par exemple, IntConsumers.


Maintenant, certes, à des fins de référence, une exécution ne le fera pas. Il y a le JIT à considérer, et le JIT ne commencera qu'après une certaine quantité d'exécutions du même chemin de code, pour une certaine définition d'un "certain montant". Donc, alors que le code ci-dessus vous donnera une indication (pour ce que cette indication vaut), vous ne devriez pas le prendre comme un absolu dans tous les cas.

Pour des fins de comparaison, pensez à utiliser jmh ... Et lire attentivement tous les documents! La machine virtuelle Java est en même temps un appareil efficace et complexe à l'exécution de code ...

1

vous pensez le long des lignes droites dans le monde Java. Toutefois, si vous ne voulez pas gâcher avec l'héritage dans ce contexte, d'une manière que vous pourriez réaliser ce que vous avez besoin est par réflexion, avec une méthode comme suit:

public static long timeit(Class c) { 
    long startTime, endTime, min; 

    // new object and the method to call 
    Object instance = c.newInstance(); 
    Method method = getClass().getDeclaredMethod("sort"); 

    min = Long.MAX_VALUE; 

    for (int i = 0; i != 10000; ++i) { 
     startTime = System.nanoTime(); 
     // call the function 
     method.invoke(obj); 
     endTime = System.nanoTime(); 
     min = endTime - startTime < min ? endTime - startTime : min; 
    } 
    return min; 
} 


//then you call it like this: 
long time1 = timeit(QuickSort.class); 

Cela dit, il y aura probablement Certains frais généraux introduits avec cette approche sont associés, mais ils devraient tout de même vous donner une bonne image comparative.

+0

La réflexion peut interférer avec JIT, ce qui peut fausser le benchmark.http://docs.oracle.com/javase/tutorial/reflect/index.html – Gene