Static functions inlining in Java
-
15-06-2021 - |
Question
First code:
public static int pitagoras(int a, int b)
{
return (int) Math.sqrt(a*a + b*b);
}
public static int distance(int x, int y, int x2, int y2)
{
return pitagoras(x - x2, y - y2);
}
distance
is called very often. When I compiled it with javac
and then decompiled with javap -c
I got this bytecode:
public static int pitagoras(int, int);
Code:
0: iload_0
1: iload_0
2: imul
3: iload_1
4: iload_1
5: imul
6: iadd
7: i2d
8: invokestatic #24; //Method java/lang/Math.sqrt:(D)D
11: d2i
12: ireturn
public static int distance(int, int, int, int);
Code:
0: iload_0
1: iload_2
2: isub
3: iload_1
4: iload_3
5: isub
6: invokestatic #34; //Method pitagoras:(II)I
9: ireturn
It seems that javac
hasn't optimized second function, distance
.
Second code, I think, faster:
public static int distance(int x, int y, int x2, int y2)
{
return (int) Math.sqrt((x - x2) * (x - x2) + (y - y2) * (y - y2));
}
And its bytecode:
public static int distance(int, int, int, int);
Code:
0: iload_0
1: iload_2
2: isub
3: iload_0
4: iload_2
5: isub
6: imul
7: iload_1
8: iload_3
9: isub
10: iload_1
11: iload_3
12: isub
13: imul
14: iadd
15: i2d
16: invokestatic #24; //Method java/lang/Math.sqrt:(D)D
19: d2i
20: ireturn
Is invokestatic
so fast that it's the same as inlining static function? Why javac
did not optimize this? Or maybe it is in fact optimized and these two codes will give the same, but I'm missing something?
Solution
javac
doesn't optimise. That's the job of the JVM implementation (typically HotSpot).
There used to be a few optimisations in javac
but they complicated the code and allegedly tended to arrange the code so that HotSpot optimisations were inhibited.
HotSpot optimisations are generally done dynamically after thousands of iterations (configurable, default dependent upon whether using "Client", "Server" or tiered versions).
There are some things that javac
is required to do by the language specification, such as inlining constants and combining literal strings.
OTHER TIPS
The Java language does not define inlined functions. Many (perhaps most) Just-In-Time (JIT) compilers will dynamically (at run time) replace such static function calls with inlined code.
I believe that performance of both version will be similar because JVM uses JIT to increase the performance.
The kind of optimization you're looking for (inlining) doesn't necessarily occur at compile time, but it's quite possible that the Just in Time (JIT) compiler will perform it during runtime.
So it's unlikely that you'll be able to see the inlining happen at the byte code level, more likely, it'll occur at the native code level during program execution.
The given answers are right : javac does not inline methods as it may not be the best thing to do.
Suppose that the distance()
method is called once in a while but not very often. Optimizing it by inlining pitagoras()
and stuff would slow down compilation for something that is barely used.
On the other hand, Hotspot knows when a method is called and how many times it is called. If the method is executed often, then Hotspot may inline it and compile it to native code, but only if it improves performances. Remember that Hotspot is the only component that knows if an optimization is a good thing or not.
Also, note that javac may do one optimization : it eliminates dead code. Consider this class :
public class Test {
public final static boolean ENABLED=false;
public static void main(String... args) {
if(ENABLED)
System.out.println("Hello World");
}
}
The compiled bytecode for the main method is this :
public static void main(java.lang.String[]);
Code:
0: return
=> javac detected that the println
line could not be reached and removed it.