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?
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.
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.
Related
Here is a simple question about Java compile optimisation.
Is
final int CONSTANT_NUMBER="Foo Bar".length();
equal to
final int CONSTANT_NUMBER=7;
on compiling code or generally in performance aspect?
No the java compiler doesn't evaluate "Foo Bar".length() at compile time.
Consider these classes
public class ConstantCheck {
final int CONSTANT_NUMBER = "Foo Bar".length();
}
and
public class ConstantCheck {
final int CONSTANT_NUMBER = 7;
}
Using javap -v on the compiled .class file you can see, that the .length() call is kept:
The former results in
...
final int CONSTANT_NUMBER;
descriptor: I
flags: ACC_FINAL
public text.ConstantCheck();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=2, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: aload_0
5: ldc #2 // String Foo Bar
7: invokevirtual #3 // Method java/lang/String.length:()I
10: putfield #4 // Field CONSTANT_NUMBER:I
13: return
...
the latter in
...
final int CONSTANT_NUMBER;
descriptor: I
flags: ACC_FINAL
ConstantValue: int 7
public text.ConstantCheck();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=2, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: aload_0
5: bipush 7
7: putfield #2 // Field CONSTANT_NUMBER:I
10: return
....
In the first case the .length call is present
7: invokevirtual #3 // Method java/lang/String.length:()I
in the second case it's just a constant that is written to the field
5: bipush 7
7: putfield #2 // Field CONSTANT_NUMBER:I
From compiling, definitely second is going to be a little faster, as it first has a method call. However, machines these days are way too fast for this to be noticeable or significant.
For performance - Modern compilers should be smart to see that "Foo Bar" is a constant and should replace the expression to length. If however, you change the line to be a variable, then you may be able to fool the compiler to call the method every time, there by making 1st slightly faster if run in a big loop.
I tested this by making them run in a big loop and second method gave slightly better performance. Guess my compiler isn't smart enough to replace the method call with a constant.
So short answer , performance wise, direct int is better than method call on my machine, but it may differ on a different compiler.
Why does Java have an IINC bytecode instruction?
There is already an IADD bytecode instruction that can be used to accomplish the same.
So why does IINC exist?
Only the original designers of Java can answer why they made particular design decisions. However, we can speculate:
IINC does not let you do anything that can't already be accomplished by a ILOAD/SIPUSH/IADD/ISTORE combo. The difference is that IINC is a single instruction, which only takes 3 or 6 bytes, while the 4 instruction sequence is obviously longer. So IINC slightly reduces the size of bytecode that uses it.
Apart from that, early versions of Java used an interpreter, where every instruction has overhead during execution. In this case, using a single IINC instruction could be faster than the equivalent alternative bytecode sequence. Note that JITting has made this largely irrelevant, but IINC dates back to the original version of Java.
As already pointed out a single iinc instruction is shorter than the a iload, sipush, iadd, istore sequence. There is also evidence, that performing a common-case code size reduction was an important motivation.
There are specialized instructions for dealing with the first four local variables, e.g. aload_0 does the same as aload 0 and it will be used often for loading the this reference on the operand stack. There’s an ldc instruction being able to refer to one of the first 255 constant pool items whereas all of them could be handled by ldc_w, branch instructions use two bytes for offsets, so only overly large methods have to resort to goto_w, and iconst_n instructions for -1 to 5 exist despite these all could be handled by bipush which supports values which all also could all be handled by sipush, which could be superseded by ldc.
So asymmetric instructions are the norm. In typical applications, there are a lot of small methods with only a few local variables and smaller numbers are more common than larger numbers. iinc is a direct equivalent to stand-alone i++ or i+=smallConstantNumber expressions (applied to local variables) which often occur within loops. By being able to express common code idioms in more compact code without loosing the ability to express all code, you’ll get great savings in overall code size.
As also already pointed out, there is only a slight opportunity for faster execution in interpreted executions which is irrelevant for compiled/optimized code execution.
Looking at this table, there are a couple important differences.
iinc: increment local variable #index by signed byte const
iinc uses a register instead of the stack.
iinc can only increment by a signed byte value. If you want to add [-128,127] to an integer, then you could use iinc, but as soon as you want to add a number outside that range you would need to use isub, iadd, or multiple iinc instructions.
E1:
TL;DR
I was basically right, except that the limit is signed short values (16 bits [-32768,32767]). There's a wide bytecode instruction which modifies iinc (and a couple other instructions) to use 16 bit numbers instead of 8 bit numbers.
Additionally, consider adding two variables together. If one of the variables is not constant, the compiler will never be able to inline its value to bytecode, so it cannot use iinc; it will have to use iadd.
package SO37056714;
public class IntegerIncrementTest {
public static void main(String[] args) {
int i = 1;
i += 5;
}
}
I'm going to be experimenting with the above piece of code. As it is, is uses iinc, as expected.
$ javap -c IntegerIncrementTest.class
Compiled from "IntegerIncrementTest.java"
public class SO37056714.IntegerIncrementTest {
public SO37056714.IntegerIncrementTest();
Code:
0: aload_0
1: invokespecial #8 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: iconst_1
1: istore_1
2: iinc 1, 5
5: return
}
i += 127 uses iinc as expected.
$ javap -c IntegerIncrementTest.class
Compiled from "IntegerIncrementTest.java"
public class SO37056714.IntegerIncrementTest {
public SO37056714.IntegerIncrementTest();
Code:
0: aload_0
1: invokespecial #8 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: iconst_1
1: istore_1
2: iinc 1, 127
5: return
}
i += 128 does not use iinc anymore, but instead iinc_w:
$ javap -c IntegerIncrementTest.class
Compiled from "IntegerIncrementTest.java"
public class SO37056714.IntegerIncrementTest {
public SO37056714.IntegerIncrementTest();
Code:
0: aload_0
1: invokespecial #8 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: iconst_1
1: istore_1
2: iinc_w 1, 128
8: return
}
i -= 601 also uses iinc_w:
$ javap -c IntegerIncrementTest.class
Compiled from "IntegerIncrementTest.java"
public class SO37056714.IntegerIncrementTest {
public SO37056714.IntegerIncrementTest();
Code:
0: aload_0
1: invokespecial #8 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: iconst_1
1: istore_1
2: iinc_w 1, -601
8: return
}
The _w suffix refers to the wide bytecode, which allows for constants up to 16 bits ([-32768, 32767]).
If we try i += 32768, we will see what I predicted above:
$ javap -c IntegerIncrementTest.class
Compiled from "IntegerIncrementTest.java"
public class SO37056714.IntegerIncrementTest {
public SO37056714.IntegerIncrementTest();
Code:
0: aload_0
1: invokespecial #8 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: iconst_1
1: istore_1
2: iload_1
3: ldc #16 // int 32768
5: iadd
6: istore_1
7: return
}
Additionally, consider the case where we are adding another variable to i (i += c). The compiler doesn't know if c is constant or not, so it cannot inline c's value to bytecode. It will use iadd for this case too:
int i = 1;
byte c = 3;
i += c;
$ javap -c IntegerIncrementTest.class
Compiled from "IntegerIncrementTest.java"
public class SO37056714.IntegerIncrementTest {
public SO37056714.IntegerIncrementTest();
Code:
0: aload_0
1: invokespecial #8 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: iconst_1
1: istore_1
2: iconst_3
3: istore_2
4: iload_1
5: iload_2
6: iadd
7: istore_1
8: return
}
This question already has answers here:
Performance difference between post- and pre- increment operators? [closed]
(2 answers)
Closed 7 years ago.
Is there in JAVA a performance difference between i++; and i--;
I'm not able to evaluate bytecode for this, and I think that simple benchmarks are not reliable because of dependence on a specific algorithm.
im not able to evaluate bytecode
Besides the duplicate which I linked and which shows some general things to consider when asking performance related questions:
Given the following sample code (System.err.println is essentially necessary so that the compiler does not optimize away the unused variable):
public class IncDec {
public static void main(String[] args) {
int i = 5;
i++;
System.err.println(i);
i--;
System.err.println(i);
}
}
Disassembled code:
> javap -c IncDec
Compiled from "IncDec.java"
public class IncDec {
public IncDec();
Code:
0: aload_0
1: invokespecial #8 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: iconst_5
1: istore_1 // int i = 5
2: iinc 1, 1 // i++
5: getstatic #16 // Field java/lang/System.err:Ljava/io/PrintStream;
8: iload_1
9: invokevirtual #22 // Method java/io/PrintStream.println:(I)V
12: iinc 1, -1 // i--
15: getstatic #16 // Field java/lang/System.err:Ljava/io/PrintStream;
18: iload_1
19: invokevirtual #22 // Method java/io/PrintStream.println:(I)V
22: return
}
So, no, there is no performance difference in this particular case on a bytecode level - both statements are compiled to the same bytecode instruction.
The JIT compiler could be free to do any additional optimization though.
In Java, there isn't a difference in speed between the two. At the most basic level, subtraction is simply addition. That is, taking the 2's complement and adding it.
I'm going to make some investigations about dividing large arrays/matrix computations among multiple threads. But I need to know the relative time complexity of Java basic operations.
For instance:
int a = 23498234;
int b = -34234;
int[] array = new int[10000];
int c = a + b; // 1
int c = array[234]; // 2
String 1 (summary of two integers) is 10+ times faster than string 2 (memory access)
or (i & 1) == 0 is 10+ faster than i % 2 == 0.
Question: Can you supppose time relations between next operations:
+, * and / operands (suppose on Integer type)
memory access
starting new thread
For performance timing, there are many confounding factors. Rather than try to get exact timings, it's better to understand what's going on and measure what you can.
The time utility will give you detailed stats on an executable, but keep in mind you're timing the JVM which is running the code, not just your code.
You might try the javap disassembler too -- ultimately you'll want to know how your individual operations break down into java bytecode, and the amount of time it takes to execute certain key bits.
Example source code:
public class T {
public static void main(String [] args) {
int x=2;
int y=3;
int z=x+y;
System.out.println(""+x);
}
}
Compiled, then disassembled:
$ javap -c T
Compiled from "T.java"
public class T {
public T();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: iconst_2
1: istore_1
2: iconst_3
3: istore_2
4: iload_1
5: iload_2
6: iadd
7: istore_3
8: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
11: new #3 // class java/lang/StringBuilder
14: dup
15: invokespecial #4 // Method java/lang/StringBuilder."<init>":()V
18: ldc #5 // String
20: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
23: iload_1
24: invokevirtual #7 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
27: invokevirtual #8 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
30: invokevirtual #9 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
33: return
}
Look at code #6 - that's where the actual addition is happening.
One thing you need to establish is how the operations you're interested in turn into bytecode.
Within the JVM itself, you can use System.getCurrentTimeMillis() as a way of timing, but it won't give you sub-ms resolution. You can also use System.nanoTime(); to get higher precision time, (in the sense that it's sub-ms resolution) but it's less accurate.
I'm trying to understand 'native code generation and execution' part of Java JITC, but having a hard time visualizing exactly what happens. E.g. say I have the following class:
class Foo
{
private int x;
public void incX()
{
x++;
}
}
javac generates the following byte code for the method:
public void incX();
Code:
Stack=3, Locals=1, Args_size=1
0: aload_0
1: dup
2: getfield #17; //Field x:I
5: iconst_1
6: iadd
7: putfield #17; //Field x:I
10: return
LineNumberTable:
line 33: 0
line 34: 10
LocalVariableTable:
Start Length Slot Name Signature
0 11 0 this LFoo;
When JITC converts this into native code, what exactly happens? And how is this native code executed by JVM?
When the method gets called sufficiently often to pass the JVM's compilation threshold, the JIT compiles the bytecode into native code, and sets it up so that calls to the function go directly to the natively compiled method.