1

I have read How do I time a method's execution in Java? and understand the usual possibilities for timing the execution of a method (start/stop timer around execution, use aspects etc.).

I was wondering if Java 8's new method references and lambdas bring any help in reaching the following.

Normal code:

String r;
r = methodCall("foo","bar");

During performance tuning, modify code to something like this, to measure the time spent executing the method

String r;
time(() -> {
  r = methodCall("foo", "bar"); // doesn't work as r needs to be effectively final
});

I see that I could do

String r;
r = time1(() -> 
    methodCall("foo", "bar)
);

(notice the missing semi-colon at the end), using a time1 method like

public static <R> R time1(Supplier<R> s){
    long start = System.currentTimeMillis();
    R result  = s.get();
    System.out.println("Execution took " + (System.currentTimeMillis() - start));
    return result;
}

or I could do

r = time2(this::methodCall, "foo", "bar");

with a time2 method like

public static <A,B,R> R time2(BiFunction<A, B, R> f, A first, B second){
    long start = System.currentTimeMillis();
    R result = f.apply(first, second);
    System.out.println("Execution took " + (System.currentTimeMillis() - start));
    return result;
}

Without return value, I can do much better

time3(() -> {
    System.out.println("hello");
});

(notice, semicolon is present) with time3 like this

public static void time3(Runnable r){
    long start = System.currentTimeMillis();
    r.run();
    System.out.println("Execution took " + (System.currentTimeMillis() - start));
}

Is there a better solution when I have a return value?

Do I need closures to be able to do that?

Community
  • 1
  • 1
Philipp
  • 4,659
  • 9
  • 48
  • 69
  • 2
    Use a profiler. Like [flight recorder / mission control](http://www.oracle.com/technetwork/java/javaseproducts/mission-control/java-mission-control-1998576.html), [visualvm](https://visualvm.github.io/), etc. – Elliott Frisch Nov 01 '16 at 02:30
  • 1
    You sound like you're looking for a callback/callable – Rogue Nov 01 '16 at 02:32
  • 1
    Why not implement both the `Supplier` and `Runnable` versions? Then you can support both cases. – Nándor Előd Fekete Nov 01 '16 at 02:41
  • 1
    Why do you think that `r = time2(this::methodCall, "foo", "bar");` is an improvement over the previous `r = time1(() -> methodCall("foo", "bar));` variant? Because you inserted line breaks in the `time1` variant? You don’t have to. Note that `time1` works with an arbitrary number of parameters. – Holger Nov 01 '16 at 10:39
  • @Holger I don't consider it an improvement. It was just for completeness. Actually the last one is what I was hoping for, but not sure if I can get that with return values. – Philipp Nov 01 '16 at 11:29
  • 1
    Where’s the difference between ` R time1(Supplier s)` and `void time3(Runnable r)`, besides the fact that `time1` supports return values? – Holger Nov 01 '16 at 12:19
  • The difference is that you need to remove the semicolon and put the assigned variable outside of the time method. So it is not so easy to add to existing code for quick measurements, as for the void version time3 where you just add the line before and after, but don't touch your existing code. – Philipp Nov 01 '16 at 12:23
  • 2
    You don’t need to remove the semicolon. You can write `r = time1(() -> { return methodCall("foo", "bar"); });`. But anyway, if *that* is your concern, it’s even less clear, why you brought up the `time2` variant in your question. Instead, you should have spent more time describing what you actually want. Look at the other comments or the posted answer. Nobody understood that your goal is to keep the source code form of the assignment. – Holger Nov 01 '16 at 14:44
  • Sorry for the confusion. I think I had seen similar construct in Scala, and thought that Java 8 might support it - after the fact I even saw a talk by Neal Gafter where he presented that construct in an early version of closures for Java – Philipp Nov 01 '16 at 16:07
  • Well, `time3(() -> { r = methodCall("foo", "bar"); });` is valid syntax, even `time3(() -> r = methodCall("foo", "bar") );` is, but it doesn’t work with *local variables*. If `r` is a *field*, that works. Maybe that’s what you saw in Neal Gafter’s talk… Or they might have considered adding support for local variables in an earlier phase of development, but decided against it later. – Holger Nov 02 '16 at 09:31

1 Answers1

8

You can still wrap method calls, and just return null for anything which produces nothing:

public <R> R timing(Supplier<R> operation) {
    long start = System.nanoTime();
    R result  = operation.get();
    System.out.printf("Execution took %dms\n", TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start));
    return result;
}

public void timing(Runnable operation) {
    this.timing(() -> { operation.run(); return null; });
}

Then the calls would be the same essentially:

String r = timing(() -> methodCall("foo", "bar"));
timing(() -> System.out.println("Hello"));

Realistically if you're wanting to utilize method parameters / references, you need to understand the relationship between Java's Function api and a method's signature.

Essentially, you can't dynamically remap all method parameters and do a direct "named" reference of a method (and varargs-itizing the arguments), the best/closest you could do would be covering up to n parameters as you saw with BiFunction (or continuing further with making custom FunctionalInterfaces. Perhaps one day we'll have a "varargs" generic operation similar to C#, but until then we cannot do an idea similar to #time2

Rogue
  • 11,105
  • 5
  • 45
  • 71