Reputation: 1113
I am trying to create a class that times the average runtime of a method. I understand how to do that by running it 100 times and taking the average of it all. Example:
private long calculateAvg(){
long totalTime = 0;
for(int i = 0; i < ITERATIONS; i++){
long startTime = System.nanoTime();
testMethod();
long endTime = System.nanoTime();
totalTime += (endTime - startTime); //divide by 1000000 to get milliseconds.
}
return (totalTime / ITERATIONS);
}
Now I can set this to work for one static method but is there a way to pass different static methods into this to calculate instead of creating one of these for each method I want to test? If not, is there a design pattern that may work here? As of now, I am creating one of these methods for every other method I want to time and it doesn't seem efficient because I am reusing so much code.
Upvotes: 4
Views: 10153
Reputation: 121
Based on your input, your method should be something like this
private long calculateAvg(Testable t){
long totalTime = 0;
for(int i = 0; i < ITERATIONS; i++){
long startTime = System.nanoTime();
t.testMethod();
long endTime = System.nanoTime();
totalTime += (endTime - startTime); //divide by 1000000 to get milliseconds.
}
return (totalTime / ITERATIONS);
}
Then, you should have an interface with a single method:
public interface Testable {
public void testMethod();
}
In your main method, you have 3 ways to pass the method. Let's say you want to test a static sort method in SelectionSort class.
First: anonymous class (the traditional way)
calculateAvg(new Testable(){
@Override
public void testMethod(){
SelectionSort.sort();
}
})
Second: Lambda (Java 8)
calculateAvg(() -> SelectionSort.sort())
Third: Method Reference Java 8 Method Reference: How to Use it
calculateAvg(SelectionSort::sort)
Upvotes: 0
Reputation: 1475
I got two solutions may help for this question:
1. Java reflection:
public long calculateAvg(Object obj, String methodName) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, ClassNotFoundException{
Method method = obj.getClass().getDeclaredMethod(methodName);
method.setAccessible(true);
long totalTime = 0;
for(int i = 0; i < ITERATIONS; i++) {
long startTime = System.nanoTime();
method.invoke(obj);
long endTime = System.nanoTime();
totalTime += (endTime - startTime); //divide by 1000000 to get milliseconds.
}
return (totalTime / ITERATIONS);
}
You can learn about proxy pattern
, you don't need to call the real method yourself, the proxy does. you can get the time before and after calling the real method. proxy pattern tutorials
Upvotes: 1
Reputation: 12501
No you cann't pass method in Java because methods are not first class citizen in Java, they are not values
. The pattern to resolve this kind of problem is to use anonymous class
. Take your case as example, you can define an interface
:
@FunctionalInterface // this annotation is available since jdk1.8
public interface Measurable {
void measure();
}
And another MeasureUtil
util class, methods inside it accept the type Measurable
:
public class MeasureUtil {
private static final long ITERATIONS = 1000;
public static long calculateAvg(Measurable measurable) {
long totalTime = 0;
for(int i = 0; i < ITERATIONS; i++){
long startTime = System.nanoTime();
measurable.measure();
long endTime = System.nanoTime();
totalTime += (endTime - startTime); //divide by 1000000 to get milliseconds.
}
return (totalTime / ITERATIONS);
}
}
Then you new
an instance of Measurable
and pass it to MeasureUtil.calculateAvg
each time:
public class Main {
public static void main(String[] args) {
long avg = MeasureUtil.calculateAvg(new Measurable() {
@Override
public void measure() {
method_under_mesure();
}
});
System.out.println("Avg: " + avg);
}
private static void method_under_mesure() {
System.out.println("call me");
}
}
Since jdk1.8 Java starts to support lambda expression
, which can make it much simpler and easier with the syntax sugar:
long avg = MeasureUtil.calculateAvg(() -> method_under_mesure());
Upvotes: 1
Reputation: 83527
You cannot "pass methods", static nor non-static. Instead, you need to create an object reference created from a class which contains the method. Then you call the method on the object reference. In order to provide different implementations of a method, you create an interface
. Then you can have many classes which implement that interface and the method which it declares. Now your timing code uses that interface to access the correct method.
public interface TheMethod {
public void foo();
}
public class Method1 implements TheMethod {
public void foo() {
// code goes here
}
}
public class Method2 implements TheMethod {
public void foo() {
// code goes here
}
}
Now modify your timing method to accept a TheMethod
instance:
private long calculateAvg(TheMethod method){
// ...
method.foo();
// ...
}
Finally, you can call this method with different instances of classes which implement TheMethod
:
TheMethod m1 = new Method1();
TheMethod m2 = new Method2();
long x = calculateAvg(m1);
long y = calculateAvg(m2);
Upvotes: 3
Reputation: 50716
You can accept and run a Runnable
argument:
private long calculateAvg(Runnable r) {
//...
long startTime = System.nanoTime();
r.run();
long endTime = System.nanoTime();
//...
}
And call it like this:
long avg1 = calculateAvg(() -> testMethod());
long avg2 = calculateAvg(() -> testMethod2());
You might also want to consider some tips from How do I write a correct micro-benchmark in Java?
Upvotes: 4