I am implementing a number of sorting algorithms in Java, and would like to be able to time and compare each algorithm. Currently, I have a script that times each algorithm separately, with an enormous amount of code redundancy:
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);
With six or seven different sorting algorithms, this starts to feel extremely inefficient. The obvious "python-esque" solution would be to define a method which takes methods as parameters and times them like this:
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);
}
However, I gather that it is inadvisable (and inconvenient) to pass methods as parameters in Java, and doing so generically might be impossible. I suppose each sorter class could inherit from an abstract class that implements a benchmarking method, but is there a better way of achieving this in Java?
However, I gather that it is inadvisable (and inconvenient) to pass methods as parameters in Java, and doing so generically might be impossible
Eh?
Since Java 8, you can pass such generic methods as arguments; and what you want here is a Runnable
.
Provided you have two methods in a same class:
public final class Foo
{
void f1() { /* whatever */ }
void f2() { /* whatever */ }
}
then you can time both in code using method references, as in:
final Runnable r1 = Foo::f1;
final Runnable r2 = Foo::f2;
and then use those method references (which are valid lambdas) into some timing code:
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));
});
After that, you can further refine your code so as to use, for instance, IntConsumer
s.
Now, admittedly, for benchmark purposes, one run won't do it. There is the JIT to consider, and the JIT will only kick in after a certain amount of executions of the same code path, for some definition of a "certain amount". So, while the code above will give you an indication (for what this indication is worth), you should not take it as an absolute in any event.
For benchmarking purposes, consider using jmh... And carefully read all documentation! The JVM is at the same time an efficient and complex apparatus at running code...