2

I have the following method in a Java app:

public void setPixel(int x, int y, int rgb) {
    if (isValidPixel(x, y)) {
        bitmap[indexOf(x, y)] = rgb;            
    }
}

When I put direct calculations instead of the method calls, like:

public void setPixel(int x, int y, int rgb) {
    if (x < width && y < height) {
        bitmap[y * width + x] = rgb;            
    }
}

the code runs with 4 to 5 milliseconds more than the first. So why?

ranieribt
  • 1,280
  • 1
  • 15
  • 33
  • Interesting. Is the code in your second example a direct replacement with the content of the `isValidPixel` and `indexOf` methods? Are you basically manually inlining the code? – Mac Oct 09 '11 at 06:11
  • 6
    4 to 5 milliseconds out of what? What's the total run-time? – Mysticial Oct 09 '11 at 06:13
  • How many times did you measure? What's the spread? – Ben Voigt Oct 09 '11 at 06:22
  • 3
    "premature optimization is the root of all evil" (Knuth) – YMomb Oct 09 '11 at 06:28
  • Your code doesn't work when you call it with `x < 0` or `y < 0`, given it's a `Public` method this indicates a lack of code quality at the expense of overall trivial performance improvement. Also, if you're going to be setting the values often you should look at using pointers to your bitmap instead as that will be slightly faster. If you're only going to be calling it in a `Private` context then you can actually get rid of the range checks entirely. – Seph Oct 09 '11 at 07:52
  • @YMomb you left out 'about 97% of the time'. He also said 'In established engineering disciplines a 12% improvement, easily obtained, is never considered marginal and I believe the same viewpoint should prevail in software engineering' (http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.103.6084&rep=rep1&type=pdf). – user207421 Oct 09 '11 at 08:59
  • @Mac Yes, the code in the 2nd sample is a replacement with the contents of the methods. – ranieribt Oct 09 '11 at 18:09
  • @Mystical No, 4 to 5 ms is the difference between the times. – ranieribt Oct 09 '11 at 18:10
  • @Ben Voigt I've measured 10 times and calculated a simple mean. – ranieribt Oct 09 '11 at 18:10
  • @YMomb I need this optimization because the setPixel(..) method is called several times to draw 2D primitives on the screen. – ranieribt Oct 09 '11 at 18:11

3 Answers3

2

If your numbers are correct and reproducible, the most probable answer is that the JIT compiler manages to compile the method but gives up on the inline statement. You could disable the JIT compiler to prove the theory.

Community
  • 1
  • 1
leonm
  • 6,454
  • 30
  • 38
  • I think when I put the direct expression in place of its containing method, it's already inline code, so must run faster. – ranieribt Oct 09 '11 at 18:24
1

It is almost certainly the case that you methods are being optimised earlier making the first case look faster.

I suggest you run both benchmarks for at least 2-10 seconds in both orders (try one first, and later second) before trying to draw any conclusions.

I would expect the first case to inline its methods and make it exactly the same as the second.

Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
0

As leonm posts it is probably JIT compiler issue.

JIT compiler works with your compiled java code (bytecode) and when some part of the code is called often, then it is translated into the binary (machine) code which is even faster than interpreted bytecode.

Bytecode which is chosen to be translated into binary code can be determined by several rules. Some of the rules are your code is in separate very short methods and code within loops.

Problem of this issue is that short methods are usually eligible for translate much earlier than inline code in some larger piece of code or method. Especially if there is no loop or is called rarely.

Don't forget that if you duplicate same inline code in several places it is JIT compiled separately, while short common method is translated preferentially.

If you disable JIT compiler then the java virtual machine compile whole your app into the binary code in startup phase and then should not matter if you call some functionality via method or as direct inline code.

If you want to test performance even with enabled JIT compiler you should perform warm up phase before testing. This warm up indicates JIT compiler that some piece of code is called often and compiler translate it into binary code at the background.

Warm up code might look like this:

public void testSetPixel() {
  // warm up
  for (int i=0; i < 1000; i++) {
    setPixel(i, i, 10);
  }
  // your regular testing code with testing setPixel(int, int, int) method
  ...
}
Jan Stanicek
  • 1,201
  • 1
  • 14
  • 30