0

Hi does anyone have any references on the relative performance/cost of Java math operators?

Ideally something like in Code Complete 2 (which I don't have on hand right now)

where it might look something like this:

  • addition: 1
  • subtraction: 2
  • multiplication: 10
  • division: 100
  • logarithm: 600
  • exponent: 500

Thanks.

information_interchange
  • 2,538
  • 6
  • 31
  • 49
  • 2
    Can you be more explicit about your question? What do those numbers mean? Does it mean something like "*division is about 100 times slower than addition*"? Sounds very platform depended if you ask me. Heavily depends on the way your exact code looks like, how many optimizations the **JVM** makes and how the resulting machine code looks like and how it gets executed by your exact processor. Definitely not easy to create meaningful benchmarks. – Zabuzard Aug 23 '17 at 02:15
  • For example note that many processors use **lookup tables** for some frequent multiplications and other tasks (they have the results just pre-computed and stored in a table), they can thus return an answer instantly. This field is so extremely full of optimizations that you can hardly control in a benchmark. – Zabuzard Aug 23 '17 at 02:16
  • Just as an example, if you write `int sum = 0; for (int i = 0; i < 100; i++) { sum++; }`, the **JVM** will convert this into the single statement `int sum = 100;` in the bytecode (*.class* file). It then looks like addition would be extremely fast. It is so easy to run into such false assumptions there. – Zabuzard Aug 23 '17 at 02:18
  • @Zabuza The JVM can't convert things like that in the .class file. The compiler can, but it doesn't. The JVM can optimizing it at runtime, but not in the .class file. – user207421 Aug 23 '17 at 02:56
  • Hmmm, thanks for the input @Zabuza. However, from my understanding isn't it taught canonically that as a general rule division is always more expensive than addition for example (in any numerical computing class)? For example, look at the asymptotic complexities here: https://en.wikipedia.org/wiki/Computational_complexity_of_mathematical_operations While I'm sure that there might be some subtleties to the performance, I think that overall there can be general rules assigned for Java – information_interchange Aug 23 '17 at 03:01
  • Well, the same observations roughly also hold for **Java** or any other language **in general**. However it is hard to give you a precise answer if your question is not precise. – Zabuzard Aug 23 '17 at 03:12
  • Your question seems to be different, but the answer is the same: Measure it properly. Java basic math operations are as fast as the CPU does them, but there are too many more things to consider. Latency, throughput, dead code elimination, .... – maaartinus Aug 24 '17 at 17:40

1 Answers1

2

I whipped together this quickie, informal, completely unscientific test code:

import java.util.function.BinaryOperator;

public class Test {
    private static void test(String desc, BinaryOperator<Double> op, double a, double b, long startIter)
    {
        long maxIter = startIter;
        long elapsed;
        do {
            maxIter *= 2;
            long start = System.currentTimeMillis();
            for (long niter = 0; niter < maxIter; ++niter) {
                double res = op.apply(a, b);
            }
            elapsed = System.currentTimeMillis() - start;
        } while (elapsed <= 10_000);
        System.out.printf("%-15s/sec\t%g\n",
            desc, (maxIter * 1000.0) / elapsed);
    }

    public static void main(String[] arg)
    {
        test("Addition (double)", (Double a, Double b) -> {
            return a + b;
        }, 483902.7743, 42347.775, 10_000_000);
        test("Subtraction (double)", (Double a, Double b) -> {
            return a - b;
        }, 483902.7743, 42347.775, 10_000_000);
        test("Multiplication (double)", (Double a, Double b) -> {
            return a * b;
        }, 483902.7743, 42347.775, 1_000_000);
        test("Division (double)", (Double a, Double b) -> {
            return a / b;
        }, 483902.7743, 42347.775, 1_000_000);
        test("Log10", (Double a, Double b) -> {
            return Math.log10(a);
        }, 483902.7743, 42347.775, 1_000_000);
        test("LogE", (Double a, Double b) -> {
            return Math.log(a);
        }, 483902.7743, 42347.775, 1_000_000);
        test("Power", (Double a, Double b) -> {
            return Math.pow(a, b);
        }, 483902.7743, 12, 100_000);
    }
}

In my environment---standard Java 8 JDK, Intel Core2 Quad Q8300 @ 2.5GHz---a representative raw output from this test is:

Addition (double)/sec   6.18619e+08
Subtraction (double)/sec    4.10651e+08
Multiplication (double)/sec 3.27010e+07
Division (double)/sec   3.22215e+07
Log10          /sec 1.99330e+07
LogE           /sec 1.99206e+07
Power          /sec 8.67870e+06

Converting to relative performance we have:

Addition          1.0
Subtraction       1.5
Multiplication   18.9
Division         19.2
Log10            31.0
LogE             31.1
Power            71.3

As usual, your mileage may vary.

Kevin Anderson
  • 4,568
  • 3
  • 13
  • 21
  • Thanks, this is more or less what I was looking for! One thing I always wonder with these though is whether it would be better to use Random to get the numbers on each iteration, since I am worried (perhaps unjustifiably) about compiler optimizations with the same number (caching) or it could just be that that number is really easy to take the log of, for example due to a lookup table (callback to what Zabuza said) – information_interchange Aug 23 '17 at 16:40
  • Yes, it would probably give a more realistic picture to use a variety of different arguments. I wouldn't expect any dramatic changes in the overall ranks, just a better estimate of the relative performance. For example, you might find that multiplication is "really" 19 or 14 additions rather than 18, but it's never going to be 3 or 4 additions, and logs might "really" be 28 or 34 additions but probably won't ever be less than a multiplication... – Kevin Anderson Aug 24 '17 at 02:50
  • 1
    -1 Try the order "Multiplication, Subtraction, Addition" to see why. Google for bi- and megamorphic call site to understand. Use JMH to do it right. – maaartinus Aug 24 '17 at 17:30