Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why this method does not get optimized away?

This Java method gets used in benchmarks for simulating slow computation:

static int slowItDown() {
    int result = 0;
    for (int i = 1; i <= 1000; i++) {
        result += i;
    }
    return result;
}

This is IMHO a very bad idea, as its body can get replaced by return 500500. This seems to never happen1; probably because of such an optimization being irrelevant for real code as Jon Skeet stated.

Interestingly, a slightly simpler method with result += 1; gets fully optimized away (caliper reports 0.460543 ns).

But even when we agree that optimizing away methods returning a constant result is useless for real code, there's still loop unrolling, which could lead to something like

static int slowItDown() {
    int result = 0;
    for (int i = 1; i <= 1000; i += 2) {
        result += 2 * i + 1;
    }
    return result;
}

So my question remains: Why is no optimization performed here?

1Contrary to what I wrote originally; I must have seen something what wasn't there.

like image 502
maaartinus Avatar asked Oct 22 '22 01:10

maaartinus


1 Answers

Well, the JVM does optimize away such code. The question is how many times it has to be detected as a real hotspot (benchmarks do some more than this single method, usually) before it will be analyzed this way. In my setup it required 16830 invocations before the execution time went to (almost) zero.

It’s correct that such a code does not appear in real code. However it might remain after several inlining operations of other hotspots dealing with values not being compiling-time constants but runtime constants or de-facto constants (values that could change in theory but don’t practically). When such a piece of code remains it’s a great benefit to optimize it away entirely but that is not expected to happen soon, i.e. when calling right from the main method.

Update: I simplified the code and the optimization came even earlier.

public static void main(String[] args) {
  final int inner=10;
  final float innerFrac=1f/inner;
  int count=0; 
  for(int j=0; j<Integer.MAX_VALUE; j++) {
    long t0=System.nanoTime();
    for(int i=0; i<inner; i++) slowItDown();
    long t1=System.nanoTime();
    count+=inner;
    final float dt = (t1-t0)*innerFrac;
    System.out.printf("execution time: %.0f ns%n", dt);
    if(dt<10) break;
  }
  System.out.println("after "+count+" invocations");
  System.out.println(System.getProperty("java.version"));
  System.out.println(System.getProperty("java.vm.version"));
}
static int slowItDown() {
  int result = 0;
  for (int i = 1; i <= 1000; i++) {
      result += i;
  }
  return result;
}

execution time: 0 ns
after 15300 invocations
1.7.0_13
23.7-b01

(64Bit Server VM)

like image 197
Holger Avatar answered Nov 15 '22 04:11

Holger