3

Inspired by another question on Stack Overflow, I have written a micro-benchmark to check, what is more efficient:

  • conditionally checking for zero divisor or
  • catching and handling an ArithmeticException

Below is my code:

@State(Scope.Thread)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public class MyBenchmark {

    private int a = 10;
    // a little bit less obvious than int b = 0;
    private int b = (int) Math.floor(Math.random());

    @Benchmark
    public float conditional() {
        if (b == 0) {
            return 0;
        } else {
            return a / b;
        }
    }

    @Benchmark
    public float exceptional() {
        try {
            return a / b;
        } catch (ArithmeticException aex) {
            return 0;
        }
    }
}

I am totally new to JMH and not sure if the code is allright.

Is my benchmark correct? Do you see any mistakes?

Side not: please don't suggest asking on https://codereview.stackexchange.com. For Codereview code must already work as intended. I am not sure this benchmark works as intended.

lexicore
  • 42,748
  • 17
  • 132
  • 221

1 Answers1

2

The big thing I see missing is any sort of randomness. That will make it easier for the branch prediction to do its work, which will make both methods faster than they probably would be in practice for division by 0.

I would do three variations of each method:

  1. with a random array with zeros intermixed, and have the benchmark be parameterized with an index into that array.
  2. with a random array of non-zero numbers
  3. with all 0s

That should give you a good idea of the overall performance, including branch prediction. For point (1), it may also be interesting to play with the ratio of 0s to non-0s.

I forget if JMH lets you parameterize directly on individual values of an array. If it does, then I'd use that. Otherwise, you'll have to parameterize on the index to that array. In that case, I would also put the all-0s in an array so that the stay access is part of all tests. I would also probably create a "control" that just accesses the array and returns its value, so that you can find out that overhead more directly.

Also, a minor nit: I don't think you need to return floats, since they'll just be converted from the ints that the division actually produces.

yshavit
  • 42,327
  • 7
  • 87
  • 124
  • 1
    Concerning parameterization, I guess, the proper way is using an `int` parameter determining the percentage, filling an array accordingly and shuffling it in the setup. In the benchmark, I'd either loop over the array or compute `index = (index+1) & array.length-1` on every step (using a power of two array length). You mat drop points 2 and 3 are they're special cases. – maaartinus Apr 18 '18 at 16:03
  • @maaartinus That's a potentially good simplification. Last I checked, though, JMH fairly strongly recommended against looping within a benchmark method. I'm not positive on the subtleties of why. – yshavit Apr 18 '18 at 16:32
  • I know; that's why I proposed the alternative. However, I guess, that there's a higher overhead there than in looping (though I hope the bounds checks are gone by now (see [this question](https://stackoverflow.com/q/21702939/581205) at the bottom). – maaartinus Apr 18 '18 at 18:06