0

Context

So I am playing with the code related to How slow are Java exceptions? by adding some method references to it. The modified code is below

/**
 * [https://stackoverflow.com/questions/299068/how-slow-are-java-exceptions]
 * 
 * The original code is credited to 
 * [https://stackoverflow.com/users/15809/mecki] &
 * [https://stackoverflow.com/users/581994/hot-licks]
 * 
 */
public class ThrowableCost {
    int value;

    public int getValue() {
        return value;
    }

    public void reset() {
        value = 0;
    }

    // This one will regularly throw one
    public void method3(int i) {
        value = ((value + i) / i) << 1;
        // i & 1 is equally fast to calculate as i & 0xFFFFFFF; it is both
        // an AND operation between two integers. The size of the number plays
        // no role. AND on 32 BIT always ANDs all 32 bits
        if ((i & 0x1) == 1) {
            throw new RuntimeException();
        }
    }

    // Similar to {@link #method3} but not throw the newly created exceptions.
    public void method5(int i) {
        value = ((value + i) / i) << 1;
        // i & 1 is equally fast to calculate as i & 0xFFFFFFF; it is both
        // an AND operation between two integers. The size of the number plays
        // no role. AND on 32 BIT always ANDs all 32 bits
        if ((i & 0x1) == 1) {
            new RuntimeException();
        }
    }

    private RuntimeException e = new RuntimeException();

    // Similar to {@link #method3} but throw the pre-created exception.
    public void method6(int i) {
        value = ((value + i) / i) << 1;
        // i & 1 is equally fast to calculate as i & 0xFFFFFFF; it is both
        // an AND operation between two integers. The size of the number plays
        // no role. AND on 32 BIT always ANDs all 32 bits
        if ((i & 0x1) == 1) {
            throw e;
        }
    }

    public static void main(String[] args) {
        System.out.println("Running...");
        ThrowableCost t = new ThrowableCost();

        invokeWithTryCatch(t, t::method3, "method3");
        invokeWithTryCatch(t, t::method5, "method5");
        invokeWithTryCatch(t, t::method6, "method6");

        System.out.println("Done!");
    }

    private static void invokeWithTryCatch(ThrowableCost t, Consumer c, String label) {
        long l = System.currentTimeMillis();
        int i;
        t.reset();
        for (i = 1; i < 100000000; i++) {
            try {
                c.accept(i);
            } catch (Exception e) {
                // Do nothing here, as we will get here
            }
        }
        l = System.currentTimeMillis() - l;
        System.out.println(String.format("%s took %d ms, result was %d", label, l, t.getValue()));
    }

}

/**
 * Similar to {@link java.util.function.Consumer} but avoid auto-boxing in our case.
 */
interface Consumer{
    void accept(int i);
}

The output is

Running...
method3 took 30088 ms, result was 2
method5 took 29948 ms, result was 2
method6 took 6298 ms, result was 2
Done!

As you can see the execution time of method6 is around 6000 ms.

However, if I

  • invoked the method6 alone (by commenting out other invokeWithTryCatchs) OR
  • invoked the method6 first (by swapping order of invokeWithTryCatchs) OR
  • invoked the method6 via instance t instead of c inside of invokeWithTryCatch

then the execution of the method6 is much shorter (i.e: around 700 ms)

Question Why does the order of invokeWithTryCatchs impact on the execution time of method6 in this approach ?

Note I tried with both JDK8 and JDK11.

  • In all probability, the longer run is long enough to trigger recompilation of `method6`. Note also https://stackoverflow.com/a/8024032/1189885 – chrylis -cautiouslyoptimistic- Nov 14 '18 at 04:12
  • Hi @chrylis, yes I am aware of [hot-licks' answer](https://stackoverflow.com/a/8024032/1189885) as the code is based on it. Would you mind to elaborate about `recompilation of method6` ? Say, instead of invoking methods via method-reference, I make it trivial as defining 3 versions of `invokeWithTryCatch` to invoke methods on `t` instance (e.g: change `c.accept(i)` to `t.method*(i)`), then the execution time of `method6` is stable at 700 ms no matter of its order? Is that in this case, my code has higher chance to be kicked out of the JVM code cache and requires recompilation ? – Grumpy Apple Nov 14 '18 at 05:12

0 Answers0