Related
OpenJDK 1.8.0_191
I compiled and decompiled a piece of code below using Fernflower.
public class Decompile {
public static void main(String[] args) {
final int VAL = 20;
System.out.println(VAL);
}
}
The output is:
public class Decompile {
public static void main(String[] args) {
boolean VAL = true;
System.out.println(20);
}
}
I'm confused, how did VAL become a boolean?
UPDATE:
In Intellij IDEA decompiled code looks like this:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
public class Decompile {
public Decompile() {
}
public static void main(String[] args) {
int VAL = true;
System.out.println(20);
}
}
The bytecode is
L0
LINENUMBER 5 L0
BIPUSH 20
ISTORE 1
L1
LINENUMBER 6 L1
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
BIPUSH 20
INVOKEVIRTUAL java/io/PrintStream.println (I)V
As you can see the BIPUSH pushes 20 onto the stack, then ISTORE takes the value and store it into the local variable.
It's a Fernflower problem.
For your interest the output for bytecode version 55 is
int VAL = true;
System.out.println(20);
You can see decompilers can be wrong :)
The underlying issue is that Java bytecode has no notion of booleans, byte, chars, or shorts (except in type signatures). All local variables with those types are instead compiled to ints. Boolean true and false are compiled to 1 and 0 respectively.
What this means is that the decompiler has to guess whether a given local variable was supposed to be a boolean or an integer type. In this case, the value 20 is stored in the variable, which will never be stored in a variable of boolean type in Java code, so it should be easy for the decompiler to guess that it is an integer type based on the context. But it appears that Fernflower's boolean guesser is not that sophisticated.
For what it's worth, this is an inherently hard problem. Especially when you consider that non-Java bytecode doesn't have to follow the same patterns that Java does. It is perfectly valid for bytecode to use the same variable in both integer and boolean contexts. The Krakatau decompiler has a pretty sophisticated inference step for guessing whether variables should be booleans or not, but it will still get things wrong in situations like this.
It works like that as compiler do some optimization during the generation of the byte code. As VAL = 20; is final and not changing, so it can put the 20 in place of VAL without impacting the functionality in the second statement. Now the decompiler has only byte code and when it goes to read the Byte code it found 20 as inline in the second line. Byte code generated by the code as below:
0: bipush 20
2: istore_1
3: getstatic #20 // Field java/lang/System.out:Ljava/io/PrintStream;
6: bipush 20
8: invokevirtual #26 // Method java/io/PrintStream.println:(I)V
In this code:
if (value >= x && value <= y) {
when value >= x and value <= y are as likely true as false with no particular pattern, would using the & operator be faster than using &&?
Specifically, I am thinking about how && lazily evaluates the right-hand-side expression (ie only if the LHS is true), which implies a conditional, whereas in Java & in this context guarantees strict evaluation of both (boolean) sub-expressions. The value result is the same either way.
But whilst a >= or <= operator will use a simple comparison instruction, the && must involve a branch, and that branch is susceptible to branch prediction failure - as per this Very Famous Question: Why is it faster to process a sorted array than an unsorted array?
So, forcing the expression to have no lazy components will surely be more deterministic and not be vulnerable to prediction failure. Right?
Notes:
obviously the answer to my question would be No if the code looked like this: if(value >= x && verySlowFunction()). I am focusing on "sufficiently simple" RHS expressions.
there's a conditional branch in there anyway (the if statement). I can't quite prove to myself that that is irrelevant, and that alternative formulations might be better examples, like boolean b = value >= x && value <= y;
this all falls into the world of horrendous micro-optimizations. Yeah, I know :-) ... interesting though?
Update
Just to explain why I'm interested: I've been staring at the systems that Martin Thompson has been writing about on his Mechanical Sympathy blog, after he came and did a talk about Aeron. One of the key messages is that our hardware has all this magical stuff in it, and we software developers tragically fail to take advantage of it. Don't worry, I'm not about to go s/&&/\&/ on all my code :-) ... but there are a number of questions on this site on improving branch prediction by removing branches, and it occurred to me that the conditional boolean operators are at the core of test conditions.
Of course, #StephenC makes the fantastic point that bending your code into weird shapes can make it less easy for JITs to spot common optimizations - if not now, then in the future. And that the Very Famous Question mentioned above is special because it pushes the prediction complexity far beyond practical optimization.
I'm pretty much aware that in most (or almost all) situations, && is the clearest, simplest, fastest, best thing to do - although I'm very grateful to the people who have posted answers demonstrating this! I'm really interested to see if there are actually any cases in anyone's experience where the answer to "Can & be faster?" might be Yes...
Update 2:
(Addressing advice that the question is overly broad. I don't want to make major changes to this question because it might compromise some of the answers below, which are of exceptional quality!) Perhaps an example in the wild is called for; this is from the Guava LongMath class (thanks hugely to #maaartinus for finding this):
public static boolean isPowerOfTwo(long x) {
return x > 0 & (x & (x - 1)) == 0;
}
See that first &? And if you check the link, the next method is called lessThanBranchFree(...), which hints that we are in branch-avoidance territory - and Guava is really widely used: every cycle saved causes sea-levels to drop visibly. So let's put the question this way: is this use of & (where && would be more normal) a real optimization?
Ok, so you want to know how it behaves at the lower level... Let's have a look at the bytecode then!
EDIT : added the generated assembly code for AMD64, at the end. Have a look for some interesting notes.
EDIT 2 (re: OP's "Update 2"): added asm code for Guava's isPowerOfTwo method as well.
Java source
I wrote these two quick methods:
public boolean AndSC(int x, int value, int y) {
return value >= x && value <= y;
}
public boolean AndNonSC(int x, int value, int y) {
return value >= x & value <= y;
}
As you can see, they are exactly the same, save for the type of AND operator.
Java bytecode
And this is the generated bytecode:
public AndSC(III)Z
L0
LINENUMBER 8 L0
ILOAD 2
ILOAD 1
IF_ICMPLT L1
ILOAD 2
ILOAD 3
IF_ICMPGT L1
L2
LINENUMBER 9 L2
ICONST_1
IRETURN
L1
LINENUMBER 11 L1
FRAME SAME
ICONST_0
IRETURN
L3
LOCALVARIABLE this Ltest/lsoto/AndTest; L0 L3 0
LOCALVARIABLE x I L0 L3 1
LOCALVARIABLE value I L0 L3 2
LOCALVARIABLE y I L0 L3 3
MAXSTACK = 2
MAXLOCALS = 4
// access flags 0x1
public AndNonSC(III)Z
L0
LINENUMBER 15 L0
ILOAD 2
ILOAD 1
IF_ICMPLT L1
ICONST_1
GOTO L2
L1
FRAME SAME
ICONST_0
L2
FRAME SAME1 I
ILOAD 2
ILOAD 3
IF_ICMPGT L3
ICONST_1
GOTO L4
L3
FRAME SAME1 I
ICONST_0
L4
FRAME FULL [test/lsoto/AndTest I I I] [I I]
IAND
IFEQ L5
L6
LINENUMBER 16 L6
ICONST_1
IRETURN
L5
LINENUMBER 18 L5
FRAME SAME
ICONST_0
IRETURN
L7
LOCALVARIABLE this Ltest/lsoto/AndTest; L0 L7 0
LOCALVARIABLE x I L0 L7 1
LOCALVARIABLE value I L0 L7 2
LOCALVARIABLE y I L0 L7 3
MAXSTACK = 3
MAXLOCALS = 4
The AndSC (&&) method generates two conditional jumps, as expected:
It loads value and x onto the stack, and jumps to L1 if value is lower. Else it keeps running the next lines.
It loads value and y onto the stack, and jumps to L1 also, if value is greater. Else it keeps running the next lines.
Which happen to be a return true in case none of the two jumps were made.
And then we have the lines marked as L1 which are a return false.
The AndNonSC (&) method, however, generates three conditional jumps!
It loads value and x onto the stack and jumps to L1 if value is lower. Because now it needs to save the result to compare it with the other part of the AND, so it has to execute either "save true" or "save false", it can't do both with the same instruction.
It loads value and y onto the stack and jumps to L1 if value is greater. Once again it needs to save true or false and that's two different lines depending on the comparison result.
Now that both comparisons are done, the code actually executes the AND operation -- and if both are true, it jumps (for a third time) to return true; or else it continues execution onto the next line to return false.
(Preliminary) Conclusion
Though I'm not that very much experienced with Java bytecode and I may have overlooked something, it seems to me that & will actually perform worse than && in every case: it generates more instructions to execute, including more conditional jumps to predict and possibly fail at.
A rewriting of the code to replace comparisons with arithmetical operations, as someone else proposed, might be a way to make & a better option, but at the cost of making the code much less clear.
IMHO it is not worth the hassle for 99% of the scenarios (it may be very well worth it for the 1% loops that need to be extremely optimized, though).
EDIT: AMD64 assembly
As noted in the comments, the same Java bytecode can lead to different machine code in different systems, so while the Java bytecode might give us a hint about which AND version performs better, getting the actual ASM as generated by the compiler is the only way to really find out.
I printed the AMD64 ASM instructions for both methods; below are the relevant lines (stripped entry points etc.).
NOTE: all methods compiled with java 1.8.0_91 unless otherwise stated.
Method AndSC with default options
# {method} {0x0000000016da0810} 'AndSC' '(III)Z' in 'AndTest'
...
0x0000000002923e3e: cmp %r8d,%r9d
0x0000000002923e41: movabs $0x16da0a08,%rax ; {metadata(method data for {method} {0x0000000016da0810} 'AndSC' '(III)Z' in 'AndTest')}
0x0000000002923e4b: movabs $0x108,%rsi
0x0000000002923e55: jl 0x0000000002923e65
0x0000000002923e5b: movabs $0x118,%rsi
0x0000000002923e65: mov (%rax,%rsi,1),%rbx
0x0000000002923e69: lea 0x1(%rbx),%rbx
0x0000000002923e6d: mov %rbx,(%rax,%rsi,1)
0x0000000002923e71: jl 0x0000000002923eb0 ;*if_icmplt
; - AndTest::AndSC#2 (line 22)
0x0000000002923e77: cmp %edi,%r9d
0x0000000002923e7a: movabs $0x16da0a08,%rax ; {metadata(method data for {method} {0x0000000016da0810} 'AndSC' '(III)Z' in 'AndTest')}
0x0000000002923e84: movabs $0x128,%rsi
0x0000000002923e8e: jg 0x0000000002923e9e
0x0000000002923e94: movabs $0x138,%rsi
0x0000000002923e9e: mov (%rax,%rsi,1),%rdi
0x0000000002923ea2: lea 0x1(%rdi),%rdi
0x0000000002923ea6: mov %rdi,(%rax,%rsi,1)
0x0000000002923eaa: jle 0x0000000002923ec1 ;*if_icmpgt
; - AndTest::AndSC#7 (line 22)
0x0000000002923eb0: mov $0x0,%eax
0x0000000002923eb5: add $0x30,%rsp
0x0000000002923eb9: pop %rbp
0x0000000002923eba: test %eax,-0x1c73dc0(%rip) # 0x0000000000cb0100
; {poll_return}
0x0000000002923ec0: retq ;*ireturn
; - AndTest::AndSC#13 (line 25)
0x0000000002923ec1: mov $0x1,%eax
0x0000000002923ec6: add $0x30,%rsp
0x0000000002923eca: pop %rbp
0x0000000002923ecb: test %eax,-0x1c73dd1(%rip) # 0x0000000000cb0100
; {poll_return}
0x0000000002923ed1: retq
Method AndSC with -XX:PrintAssemblyOptions=intel option
# {method} {0x00000000170a0810} 'AndSC' '(III)Z' in 'AndTest'
...
0x0000000002c26e2c: cmp r9d,r8d
0x0000000002c26e2f: jl 0x0000000002c26e36 ;*if_icmplt
0x0000000002c26e31: cmp r9d,edi
0x0000000002c26e34: jle 0x0000000002c26e44 ;*iconst_0
0x0000000002c26e36: xor eax,eax ;*synchronization entry
0x0000000002c26e38: add rsp,0x10
0x0000000002c26e3c: pop rbp
0x0000000002c26e3d: test DWORD PTR [rip+0xffffffffffce91bd],eax # 0x0000000002910000
0x0000000002c26e43: ret
0x0000000002c26e44: mov eax,0x1
0x0000000002c26e49: jmp 0x0000000002c26e38
Method AndNonSC with default options
# {method} {0x0000000016da0908} 'AndNonSC' '(III)Z' in 'AndTest'
...
0x0000000002923a78: cmp %r8d,%r9d
0x0000000002923a7b: mov $0x0,%eax
0x0000000002923a80: jl 0x0000000002923a8b
0x0000000002923a86: mov $0x1,%eax
0x0000000002923a8b: cmp %edi,%r9d
0x0000000002923a8e: mov $0x0,%esi
0x0000000002923a93: jg 0x0000000002923a9e
0x0000000002923a99: mov $0x1,%esi
0x0000000002923a9e: and %rsi,%rax
0x0000000002923aa1: cmp $0x0,%eax
0x0000000002923aa4: je 0x0000000002923abb ;*ifeq
; - AndTest::AndNonSC#21 (line 29)
0x0000000002923aaa: mov $0x1,%eax
0x0000000002923aaf: add $0x30,%rsp
0x0000000002923ab3: pop %rbp
0x0000000002923ab4: test %eax,-0x1c739ba(%rip) # 0x0000000000cb0100
; {poll_return}
0x0000000002923aba: retq ;*ireturn
; - AndTest::AndNonSC#25 (line 30)
0x0000000002923abb: mov $0x0,%eax
0x0000000002923ac0: add $0x30,%rsp
0x0000000002923ac4: pop %rbp
0x0000000002923ac5: test %eax,-0x1c739cb(%rip) # 0x0000000000cb0100
; {poll_return}
0x0000000002923acb: retq
Method AndNonSC with -XX:PrintAssemblyOptions=intel option
# {method} {0x00000000170a0908} 'AndNonSC' '(III)Z' in 'AndTest'
...
0x0000000002c270b5: cmp r9d,r8d
0x0000000002c270b8: jl 0x0000000002c270df ;*if_icmplt
0x0000000002c270ba: mov r8d,0x1 ;*iload_2
0x0000000002c270c0: cmp r9d,edi
0x0000000002c270c3: cmovg r11d,r10d
0x0000000002c270c7: and r8d,r11d
0x0000000002c270ca: test r8d,r8d
0x0000000002c270cd: setne al
0x0000000002c270d0: movzx eax,al
0x0000000002c270d3: add rsp,0x10
0x0000000002c270d7: pop rbp
0x0000000002c270d8: test DWORD PTR [rip+0xffffffffffce8f22],eax # 0x0000000002910000
0x0000000002c270de: ret
0x0000000002c270df: xor r8d,r8d
0x0000000002c270e2: jmp 0x0000000002c270c0
First of all, the generated ASM code differs depending on whether we choose the default AT&T syntax or the Intel syntax.
With AT&T syntax:
The ASM code is actually longer for the AndSC method, with every bytecode IF_ICMP* translated to two assembly jump instructions, for a total of 4 conditional jumps.
Meanwhile, for the AndNonSC method the compiler generates a more straight-forward code, where each bytecode IF_ICMP* is translated to only one assembly jump instruction, keeping the original count of 3 conditional jumps.
With Intel syntax:
The ASM code for AndSC is shorter, with just 2 conditional jumps (not counting the non-conditional jmp at the end). Actually it's just two CMP, two JL/E and a XOR/MOV depending on the result.
The ASM code for AndNonSC is now longer than the AndSC one! However, it has just 1 conditional jump (for the first comparison), using the registers to directly compare the first result with the second, without any more jumps.
Conclusion after ASM code analysis
At AMD64 machine-language level, the & operator seems to generate ASM code with fewer conditional jumps, which might be better for high prediction-failure rates (random values for example).
On the other hand, the && operator seems to generate ASM code with fewer instructions (with the -XX:PrintAssemblyOptions=intel option anyway), which might be better for really long loops with prediction-friendly inputs, where the fewer number of CPU cycles for each comparison can make a difference in the long run.
As I stated in some of the comments, this is going to vary greatly between systems, so if we're talking about branch-prediction optimization, the only real answer would be: it depends on your JVM implementation, your compiler, your CPU and your input data.
Addendum: Guava's isPowerOfTwo method
Here, Guava's developers have come up with a neat way of calculating if a given number is a power of 2:
public static boolean isPowerOfTwo(long x) {
return x > 0 & (x & (x - 1)) == 0;
}
Quoting OP:
is this use of & (where && would be more normal) a real optimization?
To find out if it is, I added two similar methods to my test class:
public boolean isPowerOfTwoAND(long x) {
return x > 0 & (x & (x - 1)) == 0;
}
public boolean isPowerOfTwoANDAND(long x) {
return x > 0 && (x & (x - 1)) == 0;
}
Intel's ASM code for Guava's version
# {method} {0x0000000017580af0} 'isPowerOfTwoAND' '(J)Z' in 'AndTest'
# this: rdx:rdx = 'AndTest'
# parm0: r8:r8 = long
...
0x0000000003103bbe: movabs rax,0x0
0x0000000003103bc8: cmp rax,r8
0x0000000003103bcb: movabs rax,0x175811f0 ; {metadata(method data for {method} {0x0000000017580af0} 'isPowerOfTwoAND' '(J)Z' in 'AndTest')}
0x0000000003103bd5: movabs rsi,0x108
0x0000000003103bdf: jge 0x0000000003103bef
0x0000000003103be5: movabs rsi,0x118
0x0000000003103bef: mov rdi,QWORD PTR [rax+rsi*1]
0x0000000003103bf3: lea rdi,[rdi+0x1]
0x0000000003103bf7: mov QWORD PTR [rax+rsi*1],rdi
0x0000000003103bfb: jge 0x0000000003103c1b ;*lcmp
0x0000000003103c01: movabs rax,0x175811f0 ; {metadata(method data for {method} {0x0000000017580af0} 'isPowerOfTwoAND' '(J)Z' in 'AndTest')}
0x0000000003103c0b: inc DWORD PTR [rax+0x128]
0x0000000003103c11: mov eax,0x1
0x0000000003103c16: jmp 0x0000000003103c20 ;*goto
0x0000000003103c1b: mov eax,0x0 ;*lload_1
0x0000000003103c20: mov rsi,r8
0x0000000003103c23: movabs r10,0x1
0x0000000003103c2d: sub rsi,r10
0x0000000003103c30: and rsi,r8
0x0000000003103c33: movabs rdi,0x0
0x0000000003103c3d: cmp rsi,rdi
0x0000000003103c40: movabs rsi,0x175811f0 ; {metadata(method data for {method} {0x0000000017580af0} 'isPowerOfTwoAND' '(J)Z' in 'AndTest')}
0x0000000003103c4a: movabs rdi,0x140
0x0000000003103c54: jne 0x0000000003103c64
0x0000000003103c5a: movabs rdi,0x150
0x0000000003103c64: mov rbx,QWORD PTR [rsi+rdi*1]
0x0000000003103c68: lea rbx,[rbx+0x1]
0x0000000003103c6c: mov QWORD PTR [rsi+rdi*1],rbx
0x0000000003103c70: jne 0x0000000003103c90 ;*lcmp
0x0000000003103c76: movabs rsi,0x175811f0 ; {metadata(method data for {method} {0x0000000017580af0} 'isPowerOfTwoAND' '(J)Z' in 'AndTest')}
0x0000000003103c80: inc DWORD PTR [rsi+0x160]
0x0000000003103c86: mov esi,0x1
0x0000000003103c8b: jmp 0x0000000003103c95 ;*goto
0x0000000003103c90: mov esi,0x0 ;*iand
0x0000000003103c95: and rsi,rax
0x0000000003103c98: and esi,0x1
0x0000000003103c9b: mov rax,rsi
0x0000000003103c9e: add rsp,0x50
0x0000000003103ca2: pop rbp
0x0000000003103ca3: test DWORD PTR [rip+0xfffffffffe44c457],eax # 0x0000000001550100
0x0000000003103ca9: ret
Intel's asm code for && version
# {method} {0x0000000017580bd0} 'isPowerOfTwoANDAND' '(J)Z' in 'AndTest'
# this: rdx:rdx = 'AndTest'
# parm0: r8:r8 = long
...
0x0000000003103438: movabs rax,0x0
0x0000000003103442: cmp rax,r8
0x0000000003103445: jge 0x0000000003103471 ;*lcmp
0x000000000310344b: mov rax,r8
0x000000000310344e: movabs r10,0x1
0x0000000003103458: sub rax,r10
0x000000000310345b: and rax,r8
0x000000000310345e: movabs rsi,0x0
0x0000000003103468: cmp rax,rsi
0x000000000310346b: je 0x000000000310347b ;*lcmp
0x0000000003103471: mov eax,0x0
0x0000000003103476: jmp 0x0000000003103480 ;*ireturn
0x000000000310347b: mov eax,0x1 ;*goto
0x0000000003103480: and eax,0x1
0x0000000003103483: add rsp,0x40
0x0000000003103487: pop rbp
0x0000000003103488: test DWORD PTR [rip+0xfffffffffe44cc72],eax # 0x0000000001550100
0x000000000310348e: ret
In this specific example, the JIT compiler generates far less assembly code for the && version than for Guava's & version (and, after yesterday's results, I was honestly surprised by this).
Compared to Guava's, the && version translates to 25% less bytecode for JIT to compile, 50% less assembly instructions, and only two conditional jumps (the & version has four of them).
So everything points to Guava's & method being less efficient than the more "natural" && version.
... Or is it?
As noted before, I'm running the above examples with Java 8:
C:\....>java -version
java version "1.8.0_91"
Java(TM) SE Runtime Environment (build 1.8.0_91-b14)
Java HotSpot(TM) 64-Bit Server VM (build 25.91-b14, mixed mode)
But what if I switch to Java 7?
C:\....>c:\jdk1.7.0_79\bin\java -version
java version "1.7.0_79"
Java(TM) SE Runtime Environment (build 1.7.0_79-b15)
Java HotSpot(TM) 64-Bit Server VM (build 24.79-b02, mixed mode)
C:\....>c:\jdk1.7.0_79\bin\java -XX:+UnlockDiagnosticVMOptions -XX:CompileCommand=print,*AndTest.isPowerOfTwoAND -XX:PrintAssemblyOptions=intel AndTestMain
.....
0x0000000002512bac: xor r10d,r10d
0x0000000002512baf: mov r11d,0x1
0x0000000002512bb5: test r8,r8
0x0000000002512bb8: jle 0x0000000002512bde ;*ifle
0x0000000002512bba: mov eax,0x1 ;*lload_1
0x0000000002512bbf: mov r9,r8
0x0000000002512bc2: dec r9
0x0000000002512bc5: and r9,r8
0x0000000002512bc8: test r9,r9
0x0000000002512bcb: cmovne r11d,r10d
0x0000000002512bcf: and eax,r11d ;*iand
0x0000000002512bd2: add rsp,0x10
0x0000000002512bd6: pop rbp
0x0000000002512bd7: test DWORD PTR [rip+0xffffffffffc0d423],eax # 0x0000000002120000
0x0000000002512bdd: ret
0x0000000002512bde: xor eax,eax
0x0000000002512be0: jmp 0x0000000002512bbf
.....
Surprise! The assembly code generated for the & method by the JIT compiler in Java 7, has only one conditional jump now, and is way shorter! Whereas the && method (you'll have to trust me on this one, I don't want to clutter the ending!) remains about the same, with its two conditional jumps and a couple less instructions, tops.
Looks like Guava's engineers knew what they were doing, after all! (if they were trying to optimize Java 7 execution time, that is ;-)
So back to OP's latest question:
is this use of & (where && would be more normal) a real optimization?
And IMHO the answer is the same, even for this (very!) specific scenario: it depends on your JVM implementation, your compiler, your CPU and your input data.
For those kind of questions you should run a microbenchmark. I used JMH for this test.
The benchmarks are implemented as
// boolean logical AND
bh.consume(value >= x & y <= value);
and
// conditional AND
bh.consume(value >= x && y <= value);
and
// bitwise OR, as suggested by Joop Eggen
bh.consume(((value - x) | (y - value)) >= 0)
With values for value, x and y according to the benchmark name.
The result (five warmup and ten measurement iterations) for throughput benchmarking is:
Benchmark Mode Cnt Score Error Units
Benchmark.isBooleanANDBelowRange thrpt 10 386.086 ▒ 17.383 ops/us
Benchmark.isBooleanANDInRange thrpt 10 387.240 ▒ 7.657 ops/us
Benchmark.isBooleanANDOverRange thrpt 10 381.847 ▒ 15.295 ops/us
Benchmark.isBitwiseORBelowRange thrpt 10 384.877 ▒ 11.766 ops/us
Benchmark.isBitwiseORInRange thrpt 10 380.743 ▒ 15.042 ops/us
Benchmark.isBitwiseOROverRange thrpt 10 383.524 ▒ 16.911 ops/us
Benchmark.isConditionalANDBelowRange thrpt 10 385.190 ▒ 19.600 ops/us
Benchmark.isConditionalANDInRange thrpt 10 384.094 ▒ 15.417 ops/us
Benchmark.isConditionalANDOverRange thrpt 10 380.913 ▒ 5.537 ops/us
The result is not that different for the evaluation itself. As long no perfomance impact is spotted on that piece of code I would not try to optimize it. Depending on the place in the code the hotspot compiler might decide to do some optimization. Which probably is not covered by the above benchmarks.
some references:
boolean logical AND - the result value is true if both operand values are true; otherwise, the result is false
conditional AND - is like &, but evaluates its right-hand operand only if the value of its left-hand operand is true
bitwise OR - the result value is the bitwise inclusive OR of the operand values
I'm going to come at this from a different angle.
Consider these two code fragments,
if (value >= x && value <= y) {
and
if (value >= x & value <= y) {
If we assume that value, x, y have a primitive type, then those two (partial) statements will give the same outcome for all possible input values. (If wrapper types are involved, then they are not exactly equivalent because of an implicit null test for y that might fail in the & version and not the && version.)
If the JIT compiler is doing a good job, its optimizer will be able to deduce that those two statements do the same thing:
If one is predictably faster than the other, then it should be able to use the faster version ... in the JIT compiled code.
If not, then it doesn't matter which version is used at the source code level.
Since the JIT compiler gathers path statistics before compiling, it can potentially have more information about the execution characteristics that the programmer(!).
If the current generation JIT compiler (on any given platform) doesn't optimize well enough to handle this, the next generation could well do ... depending on whether or not empirical evidence points to this being a worthwhile pattern to optimize.
Indeed, if you write you Java code in a way that optimizes for this, there is a chance that by picking the more "obscure" version of the code, you might inhibit the current or future JIT compiler's ability to optimize.
In short, I don't think you should do this kind of micro-optimization at the source code level. And if you accept this argument1, and follow it to its logical conclusion, the question of which version is faster is ... moot2.
1 - I do not claim this is anywhere near being a proof.
2 - Unless you are one of the tiny community of people who actually write Java JIT compilers ...
The "Very Famous Question" is interesting in two respects:
On the one hand, that is an example where the kind of optimization required to make a difference is way beyond the capability of a JIT compiler.
On the other hand, it would not necessarily be the correct thing to sort the array ... just because a sorted array can be processed faster. The cost of sorting the array, could well be (much) greater than the saving.
Using either & or && still requires a condition to be evaluated so it's unlikely it will save any processing time - it might even add to it considering you're evaluating both expressions when you only need to evaluate one.
Using & over && to save a nanosecond if that in some very rare situations is pointless, you've already wasted more time contemplating the difference than you would've saved using & over &&.
Edit
I got curious and decided to run some bench marks.
I made this class:
public class Main {
static int x = 22, y = 48;
public static void main(String[] args) {
runWithOneAnd(30);
runWithTwoAnds(30);
}
static void runWithOneAnd(int value){
if(value >= x & value <= y){
}
}
static void runWithTwoAnds(int value){
if(value >= x && value <= y){
}
}
}
and ran some profiling tests with NetBeans. I didn't use any print statements to save processing time, just know both evaluate to true.
First test:
Second test:
Third test:
As you can see by the profiling tests, using only one & actually takes 2-3 times longer to run compared to using two &&. This does strike as some what odd as i did expect better performance from only one &.
I'm not 100% sure why. In both cases, both expressions have to be evaluated because both are true. I suspect that the JVM does some special optimization behind the scenes to speed it up.
Moral of the story: convention is good and premature optimization is bad.
Edit 2
I redid the benchmark code with #SvetlinZarev's comments in mind and a few other improvements. Here is the modified benchmark code:
public class Main {
static int x = 22, y = 48;
public static void main(String[] args) {
oneAndBothTrue();
oneAndOneTrue();
oneAndBothFalse();
twoAndsBothTrue();
twoAndsOneTrue();
twoAndsBothFalse();
System.out.println(b);
}
static void oneAndBothTrue() {
int value = 30;
for (int i = 0; i < 2000; i++) {
if (value >= x & value <= y) {
doSomething();
}
}
}
static void oneAndOneTrue() {
int value = 60;
for (int i = 0; i < 4000; i++) {
if (value >= x & value <= y) {
doSomething();
}
}
}
static void oneAndBothFalse() {
int value = 100;
for (int i = 0; i < 4000; i++) {
if (value >= x & value <= y) {
doSomething();
}
}
}
static void twoAndsBothTrue() {
int value = 30;
for (int i = 0; i < 4000; i++) {
if (value >= x & value <= y) {
doSomething();
}
}
}
static void twoAndsOneTrue() {
int value = 60;
for (int i = 0; i < 4000; i++) {
if (value >= x & value <= y) {
doSomething();
}
}
}
static void twoAndsBothFalse() {
int value = 100;
for (int i = 0; i < 4000; i++) {
if (value >= x & value <= y) {
doSomething();
}
}
}
//I wanted to avoid print statements here as they can
//affect the benchmark results.
static StringBuilder b = new StringBuilder();
static int times = 0;
static void doSomething(){
times++;
b.append("I have run ").append(times).append(" times \n");
}
}
And here are the performance tests:
Test 1:
Test 2:
Test 3:
This takes into account different values and different conditions as well.
Using one & takes more time to run when both conditions are true, about 60% or 2 milliseconds more time. When either one or both conditions are false, then one & runs faster, but it only runs about 0.30-0.50 milliseconds faster. So & will run faster than && in most circumstances, but the performance difference is still negligible.
What you are after is something like this:
x <= value & value <= y
value - x >= 0 & y - value >= 0
((value - x) | (y - value)) >= 0 // integer bit-or
Interesting, one would almost like to look at the byte code.
But hard to say. I wish this were a C question.
I was curious to the answer as well, so I wrote the following (simple) test for this:
private static final int max = 80000;
private static final int size = 100000;
private static final int x = 1500;
private static final int y = 15000;
private Random random;
#Before
public void setUp() {
this.random = new Random();
}
#After
public void tearDown() {
random = null;
}
#Test
public void testSingleOperand() {
int counter = 0;
int[] numbers = new int[size];
for (int j = 0; j < size; j++) {
numbers[j] = random.nextInt(max);
}
long start = System.nanoTime(); //start measuring after an array has been filled
for (int i = 0; i < numbers.length; i++) {
if (numbers[i] >= x & numbers[i] <= y) {
counter++;
}
}
long end = System.nanoTime();
System.out.println("Duration of single operand: " + (end - start));
}
#Test
public void testDoubleOperand() {
int counter = 0;
int[] numbers = new int[size];
for (int j = 0; j < size; j++) {
numbers[j] = random.nextInt(max);
}
long start = System.nanoTime(); //start measuring after an array has been filled
for (int i = 0; i < numbers.length; i++) {
if (numbers[i] >= x & numbers[i] <= y) {
counter++;
}
}
long end = System.nanoTime();
System.out.println("Duration of double operand: " + (end - start));
}
With the end result being that the comparison with && always wins in terms of speed, being about 1.5/2 milliseconds quicker than &.
EDIT:
As #SvetlinZarev pointed out, I was also measuring the time it took Random to get an integer. Changed it to use a pre-filled array of random numbers, which caused the duration of the single operand test to wildly fluctuate; the differences between several runs were up to 6-7ms.
The way this was explained to me, is that && will return false if the first check in a series is false, while & checks all items in a series regardless of how many are false. I.E.
if (x>0 && x <=10 && x
Will run faster than
if (x>0 & x <=10 & x
If x is greater than 10, because single ampersands will continue to check the rest of the conditions whereas double ampersands will break after the first non-true condition.
This isn't meant to be subjective, I am looking for reasons based on resource utilisation, compiler performance, GC performance etc. rather than elegance. Oh, and the position of brackets doesn't count, so no stylistic comments please.
Take the following loop;
Integer total = new Integer(0);
Integer i;
for (String str : string_list)
{
i = Integer.parse(str);
total += i;
}
versus...
Integer total = 0;
for (String str : string_list)
{
Integer i = Integer.parse(str);
total += i;
}
In the first one i is function scoped whereas in the second it is scoped in the loop. I have always thought (believed) that the first one would be more efficient because it just references an existing variable already allocated on the stack, whereas the second one would be pushing and popping i each iteration of the loop.
There are quite a lot of other cases where I tend to scope variables more broadly than perhaps necessary so I thought I would ask here to clear up a gap in my knowledge. Also notice that assignment of the variable on initialisation either involving the new operator or not. Do any of these sorts of semi-stylistic semi-optimisations make any difference at all?
The second one is what I would prefer. There is no functional difference other than the scoping.
Setting the same variable in each iteration makes no difference because Integer is an immutable class. Now, if you were modifying an object instead of creating a new one each time, then there would be a difference.
And as a side note, in this code you should be using int and Integer.parseInt() rather than Integer and Integer.parse(). You're introducing quite a bit of unnecessary boxing and unboxing.
Edit: It's been a while since I mucked around in bytecode, so I thought I'd get my hands dirty again.
Here's the test class I compiled:
class ScopeTest {
public void outside(String[] args) {
Integer total = 0;
Integer i;
for (String str : args)
{
i = Integer.valueOf(str);
total += i;
}
}
public void inside(String[] args) {
Integer total = 0;
for (String str : args)
{
Integer i = Integer.valueOf(str);
total += i;
}
}
}
Bytecode output (retrieved with javap -c ScopeTest after compiling):
Compiled from "ScopeTest.java"
class ScopeTest extends java.lang.Object{
ScopeTest();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: return
public void outside(java.lang.String[]);
Code:
0: iconst_0
1: invokestatic #2; //Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
4: astore_2
5: aload_1
6: astore 4
8: aload 4
10: arraylength
11: istore 5
13: iconst_0
14: istore 6
16: iload 6
18: iload 5
20: if_icmpge 55
23: aload 4
25: iload 6
27: aaload
28: astore 7
30: aload 7
32: invokestatic #3; //Method java/lang/Integer.valueOf:(Ljava/lang/String;)Ljava/lang/Integer;
35: astore_3
36: aload_2
37: invokevirtual #4; //Method java/lang/Integer.intValue:()I
40: aload_3
41: invokevirtual #4; //Method java/lang/Integer.intValue:()I
44: iadd
45: invokestatic #2; //Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
48: astore_2
49: iinc 6, 1
52: goto 16
55: return
public void inside(java.lang.String[]);
Code:
0: iconst_0
1: invokestatic #2; //Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
4: astore_2
5: aload_1
6: astore_3
7: aload_3
8: arraylength
9: istore 4
11: iconst_0
12: istore 5
14: iload 5
16: iload 4
18: if_icmpge 54
21: aload_3
22: iload 5
24: aaload
25: astore 6
27: aload 6
29: invokestatic #3; //Method java/lang/Integer.valueOf:(Ljava/lang/String;)Ljava/lang/Integer;
32: astore 7
34: aload_2
35: invokevirtual #4; //Method java/lang/Integer.intValue:()I
38: aload 7
40: invokevirtual #4; //Method java/lang/Integer.intValue:()I
43: iadd
44: invokestatic #2; //Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
47: astore_2
48: iinc 5, 1
51: goto 14
54: return
}
Contrary to my expectations, there was one difference between the two: in outside(), the variable i still took up a register even though it was omitted from the actual code (note that all the iload and istore instructions point one register higher).
The JIT compiler should make short work of this difference, but still you can see that limiting scope is a good practice.
(And with regards to my earlier side note, you can see that to add two Integer objects, Java must unbox both with intValue, add them, and then create a new Integer with valueOf. Don't do this unless absolutely necessary, because it's senseless and slower.)
The second one is far better because the first style is should only be used in C code as its mandatory. Java allows for inline declarations to minimize the scope of variables and you should take advantage of that. But you code can be further improved:
int total = 0;
for (String str: stringList) {
try {
total += Integer.valueOf(str);
} catch(NumberFormationException nfe) {
// more code to deal with the error
}
}
That follows the Java code style convention. Read the full guide here:
http://java.sun.com/docs/codeconv/html/CodeConvTOC.doc.html
It makes no significant difference apart from on the last iteration, when the reference is cleared quicker in the second example (and that would be my preference - not so much for that reason, but clarity.)
Keep the scope to the minimum possible. The hotspot VM does escape analysis to determine when references are no longer accessible, and on the basis of this allocates some objects on the stack rather than on the heap. Keeping scope as small as possible aids this process.
I would ask why you're using Integer instead of a simple int...or perhaps it's just by way of example?
The second is far better. Scoping variables as narrowly as possible makes the code far easier to read and maintain, which are much more important overall than the performance differences between these examples, which are trivial and easily optimized away.
Neither. Integer.valueOf(0); will use a reference to a cached 0. :)
Well, in this case, you're instantiating an Integer primitive every single time you say i = Integer.parseInt(str) (where i is an Integer), so (unless Java knows how to optimize it), both cases are almost equally inefficient. Consider using int instead:
int total = 0;
for (String str : string_list)
{
int i = Integer.parseInt(str);
total += i;
}
Now we're back to the question of whether to put the int declaration on the inside or outside. Assuming the Java compiler has a lick of decent optimization, I'd say it doesn't matter. Efficiency aside, it is considered good practice to declare variables as close as possible to their use.
The second one is the preferable of the two for readability, maintainability, and efficiency.
All three of these goals are achieved because you are succinctly explaining what you are doing and how your variables are being used. You are explaining this clearly to both developers and the compiler. When the variable i is defined in the for block everyone knows that it is safe to ignore it outside of the block and that the value is only valid for this iteration of the block. This will lead the to the Garbage Collector being able to be able to easily mark this memory to be freed.
I would suggest not using Integer for intermediate values. Accumulate the total as an int and after the loop create the Object or depend on auto-boxing.
Assuming you have positive numbers in your list and you're serious with
I am looking for reasons based on
resource utilisation, compiler
performance, GC performance etc.
rather than elegance.
You should implement it by yourself like:
import java.util.ArrayList;
import java.util.List;
public class Int {
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
list.add("10");
list.add("20");
int total = 0;
for (String str : list) {
int val = 0;
for (char c : str.toCharArray()) {
val = val * 10 + (int) c - 48;
}
total += val;
}
System.out.print(total);
}
}
The only GC relevant thing would be toCharArray() which could be replaced by another loop using charAt()
The question of what variable scope to use is a readability issue more than anything else. The code is better understood when every variable is restricted to the scope where it is actually used.
Now, if we inspect the technical consequences of using wide/narrow scopes, I believe that there IS a performance/footpring advantage with narrow scopes. Consider the following method, where we have 3 local variables, belonging to one global scope:
private static Random rnd = new Random();
public static void test() {
int x = rnd.nextInt();
System.out.println(x);
int y = rnd.nextInt();
System.out.println(y);
int z = rnd.nextInt();
System.out.println(z);
}
If you diassemble this code (using javap -c -verbose {class name} for example), you will see that the compiler reserves 3 slots for local variables in the stack frame structure of the test() method.
Now, suppose that we add some artificial scopes:
public static void test() {
{
int x = rnd.nextInt();
System.out.println(x);
}
{
int y = rnd.nextInt();
System.out.println(y);
}
{
int z = rnd.nextInt();
System.out.println(z);
}
}
If you diassemble the code now, you will notice that the compiler reserves only 1 slot for local variables. Since the scopes are completely independent, each time x,y or z are used, the same slot #0 is used.
What does it mean?
1) Narrow scopes save stack space
2) If we are dealing with object variables, it means that the objects may become unreachable faster, therefore are eligible for GC sooner than otherwise.
Again,note that these 2 "advantages" are really minor, and the readability concern should be by far the most important concern.
Second one since you want to keep the scope of your variables as "inner" as possible. The advantage of smaller scope is less chance for collision. In your example, there's only a few lines so the advantage might not be so obvious. But if it's larger, having the smaller-scope variables definitely is more beneficial. If someone else later has to look at the code, they would have to scan all the way back to right outside the method definition to know what i is. The argument is not much different than that of why we want to avoid global variable.
I have always wondered if, in general, declaring a throw-away variable before a loop, as opposed to repeatedly inside the loop, makes any (performance) difference?
A (quite pointless) example in Java:
a) declaration before loop:
double intermediateResult;
for(int i=0; i < 1000; i++){
intermediateResult = i;
System.out.println(intermediateResult);
}
b) declaration (repeatedly) inside loop:
for(int i=0; i < 1000; i++){
double intermediateResult = i;
System.out.println(intermediateResult);
}
Which one is better, a or b?
I suspect that repeated variable declaration (example b) creates more overhead in theory, but that compilers are smart enough so that it doesn't matter. Example b has the advantage of being more compact and limiting the scope of the variable to where it is used. Still, I tend to code according example a.
Edit: I am especially interested in the Java case.
Which is better, a or b?
From a performance perspective, you'd have to measure it. (And in my opinion, if you can measure a difference, the compiler isn't very good).
From a maintenance perspective, b is better. Declare and initialize variables in the same place, in the narrowest scope possible. Don't leave a gaping hole between the declaration and the initialization, and don't pollute namespaces you don't need to.
Well I ran your A and B examples 20 times each, looping 100 million times.(JVM - 1.5.0)
A: average execution time: .074 sec
B: average execution time : .067 sec
To my surprise B was slightly faster.
As fast as computers are now its hard to say if you could accurately measure this.
I would code it the A way as well but I would say it doesn't really matter.
It depends on the language and the exact use. For instance, in C# 1 it made no difference. In C# 2, if the local variable is captured by an anonymous method (or lambda expression in C# 3) it can make a very signficant difference.
Example:
using System;
using System.Collections.Generic;
class Test
{
static void Main()
{
List<Action> actions = new List<Action>();
int outer;
for (int i=0; i < 10; i++)
{
outer = i;
int inner = i;
actions.Add(() => Console.WriteLine("Inner={0}, Outer={1}", inner, outer));
}
foreach (Action action in actions)
{
action();
}
}
}
Output:
Inner=0, Outer=9
Inner=1, Outer=9
Inner=2, Outer=9
Inner=3, Outer=9
Inner=4, Outer=9
Inner=5, Outer=9
Inner=6, Outer=9
Inner=7, Outer=9
Inner=8, Outer=9
Inner=9, Outer=9
The difference is that all of the actions capture the same outer variable, but each has its own separate inner variable.
The following is what I wrote and compiled in .NET.
double r0;
for (int i = 0; i < 1000; i++) {
r0 = i*i;
Console.WriteLine(r0);
}
for (int j = 0; j < 1000; j++) {
double r1 = j*j;
Console.WriteLine(r1);
}
This is what I get from .NET Reflector when CIL is rendered back into code.
for (int i = 0; i < 0x3e8; i++)
{
double r0 = i * i;
Console.WriteLine(r0);
}
for (int j = 0; j < 0x3e8; j++)
{
double r1 = j * j;
Console.WriteLine(r1);
}
So both look exactly same after compilation. In managed languages code is converted into CL/byte code and at time of execution it's converted into machine language. So in machine language a double may not even be created on the stack. It may just be a register as code reflect that it is a temporary variable for WriteLine function. There are a whole set optimization rules just for loops. So the average guy shouldn't be worried about it, especially in managed languages. There are cases when you can optimize manage code, for example, if you have to concatenate a large number of strings using just string a; a+=anotherstring[i] vs using StringBuilder. There is very big difference in performance between both. There are a lot of such cases where the compiler cannot optimize your code, because it cannot figure out what is intended in a bigger scope. But it can pretty much optimize basic things for you.
This is a gotcha in VB.NET. The Visual Basic result won't reinitialize the variable in this example:
For i as Integer = 1 to 100
Dim j as Integer
Console.WriteLine(j)
j = i
Next
' Output: 0 1 2 3 4...
This will print 0 the first time (Visual Basic variables have default values when declared!) but i each time after that.
If you add a = 0, though, you get what you might expect:
For i as Integer = 1 to 100
Dim j as Integer = 0
Console.WriteLine(j)
j = i
Next
'Output: 0 0 0 0 0...
I made a simple test:
int b;
for (int i = 0; i < 10; i++) {
b = i;
}
vs
for (int i = 0; i < 10; i++) {
int b = i;
}
I compiled these codes with gcc - 5.2.0. And then I disassembled the main ()
of these two codes and that's the result:
1º:
0x00000000004004b6 <+0>: push rbp
0x00000000004004b7 <+1>: mov rbp,rsp
0x00000000004004ba <+4>: mov DWORD PTR [rbp-0x4],0x0
0x00000000004004c1 <+11>: jmp 0x4004cd <main+23>
0x00000000004004c3 <+13>: mov eax,DWORD PTR [rbp-0x4]
0x00000000004004c6 <+16>: mov DWORD PTR [rbp-0x8],eax
0x00000000004004c9 <+19>: add DWORD PTR [rbp-0x4],0x1
0x00000000004004cd <+23>: cmp DWORD PTR [rbp-0x4],0x9
0x00000000004004d1 <+27>: jle 0x4004c3 <main+13>
0x00000000004004d3 <+29>: mov eax,0x0
0x00000000004004d8 <+34>: pop rbp
0x00000000004004d9 <+35>: ret
vs
2º
0x00000000004004b6 <+0>: push rbp
0x00000000004004b7 <+1>: mov rbp,rsp
0x00000000004004ba <+4>: mov DWORD PTR [rbp-0x4],0x0
0x00000000004004c1 <+11>: jmp 0x4004cd <main+23>
0x00000000004004c3 <+13>: mov eax,DWORD PTR [rbp-0x4]
0x00000000004004c6 <+16>: mov DWORD PTR [rbp-0x8],eax
0x00000000004004c9 <+19>: add DWORD PTR [rbp-0x4],0x1
0x00000000004004cd <+23>: cmp DWORD PTR [rbp-0x4],0x9
0x00000000004004d1 <+27>: jle 0x4004c3 <main+13>
0x00000000004004d3 <+29>: mov eax,0x0
0x00000000004004d8 <+34>: pop rbp
0x00000000004004d9 <+35>: ret
Which are exaclty the same asm result. isn't a proof that the two codes produce the same thing?
It is language dependent - IIRC C# optimises this, so there isn't any difference, but JavaScript (for example) will do the whole memory allocation shebang each time.
I would always use A (rather than relying on the compiler) and might also rewrite to:
for(int i=0, double intermediateResult=0; i<1000; i++){
intermediateResult = i;
System.out.println(intermediateResult);
}
This still restricts intermediateResult to the loop's scope, but doesn't redeclare during each iteration.
In my opinion, b is the better structure. In a, the last value of intermediateResult sticks around after your loop is finished.
Edit:
This doesn't make a lot of difference with value types, but reference types can be somewhat weighty. Personally, I like variables to be dereferenced as soon as possible for cleanup, and b does that for you,
I suspect a few compilers could optimize both to be the same code, but certainly not all. So I'd say you're better off with the former. The only reason for the latter is if you want to ensure that the declared variable is used only within your loop.
As a general rule, I declare my variables in the inner-most possible scope. So, if you're not using intermediateResult outside of the loop, then I'd go with B.
A co-worker prefers the first form, telling it is an optimization, preferring to re-use a declaration.
I prefer the second one (and try to persuade my co-worker! ;-)), having read that:
It reduces scope of variables to where they are needed, which is a good thing.
Java optimizes enough to make no significant difference in performance. IIRC, perhaps the second form is even faster.
Anyway, it falls in the category of premature optimization that rely in quality of compiler and/or JVM.
There is a difference in C# if you are using the variable in a lambda, etc. But in general the compiler will basically do the same thing, assuming the variable is only used within the loop.
Given that they are basically the same: Note that version b makes it much more obvious to readers that the variable isn't, and can't, be used after the loop. Additionally, version b is much more easily refactored. It is more difficult to extract the loop body into its own method in version a. Moreover, version b assures you that there is no side effect to such a refactoring.
Hence, version a annoys me to no end, because there's no benefit to it and it makes it much more difficult to reason about the code...
Well, you could always make a scope for that:
{ //Or if(true) if the language doesn't support making scopes like this
double intermediateResult;
for (int i=0; i<1000; i++) {
intermediateResult = i;
System.out.println(intermediateResult);
}
}
This way you only declare the variable once, and it'll die when you leave the loop.
I think it depends on the compiler and is hard to give a general answer.
I've always thought that if you declare your variables inside of your loop then you're wasting memory. If you have something like this:
for(;;) {
Object o = new Object();
}
Then not only does the object need to be created for each iteration, but there needs to be a new reference allocated for each object. It seems that if the garbage collector is slow then you'll have a bunch of dangling references that need to be cleaned up.
However, if you have this:
Object o;
for(;;) {
o = new Object();
}
Then you're only creating a single reference and assigning a new object to it each time. Sure, it might take a bit longer for it to go out of scope, but then there's only one dangling reference to deal with.
My practice is following:
if type of variable is simple (int, double, ...) I prefer variant b (inside).
Reason: reducing scope of variable.
if type of variable is not simple (some kind of class or struct) I prefer variant a (outside).
Reason: reducing number of ctor-dtor calls.
I had this very same question for a long time. So I tested an even simpler piece of code.
Conclusion: For such cases there is NO performance difference.
Outside loop case
int intermediateResult;
for(int i=0; i < 1000; i++){
intermediateResult = i+2;
System.out.println(intermediateResult);
}
Inside loop case
for(int i=0; i < 1000; i++){
int intermediateResult = i+2;
System.out.println(intermediateResult);
}
I checked the compiled file on IntelliJ's decompiler and for both cases, I got the same Test.class
for(int i = 0; i < 1000; ++i) {
int intermediateResult = i + 2;
System.out.println(intermediateResult);
}
I also disassembled code for both the case using the method given in this answer. I'll show only the parts relevant to the answer
Outside loop case
Code:
stack=2, locals=3, args_size=1
0: iconst_0
1: istore_2
2: iload_2
3: sipush 1000
6: if_icmpge 26
9: iload_2
10: iconst_2
11: iadd
12: istore_1
13: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
16: iload_1
17: invokevirtual #3 // Method java/io/PrintStream.println:(I)V
20: iinc 2, 1
23: goto 2
26: return
LocalVariableTable:
Start Length Slot Name Signature
13 13 1 intermediateResult I
2 24 2 i I
0 27 0 args [Ljava/lang/String;
Inside loop case
Code:
stack=2, locals=3, args_size=1
0: iconst_0
1: istore_1
2: iload_1
3: sipush 1000
6: if_icmpge 26
9: iload_1
10: iconst_2
11: iadd
12: istore_2
13: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
16: iload_2
17: invokevirtual #3 // Method java/io/PrintStream.println:(I)V
20: iinc 1, 1
23: goto 2
26: return
LocalVariableTable:
Start Length Slot Name Signature
13 7 2 intermediateResult I
2 24 1 i I
0 27 0 args [Ljava/lang/String;
If you pay close attention, only the Slot assigned to i and intermediateResult in LocalVariableTable is swapped as a product of their order of appearance. The same difference in slot is reflected in other lines of code.
No extra operation is being performed
intermediateResult is still a local variable in both cases, so there is no difference access time.
BONUS
Compilers do a ton of optimization, take a look at what happens in this case.
Zero work case
for(int i=0; i < 1000; i++){
int intermediateResult = i;
System.out.println(intermediateResult);
}
Zero work decompiled
for(int i = 0; i < 1000; ++i) {
System.out.println(i);
}
From a performance perspective, outside is (much) better.
public static void outside() {
double intermediateResult;
for(int i=0; i < Integer.MAX_VALUE; i++){
intermediateResult = i;
}
}
public static void inside() {
for(int i=0; i < Integer.MAX_VALUE; i++){
double intermediateResult = i;
}
}
I executed both functions 1 billion times each.
outside() took 65 milliseconds. inside() took 1.5 seconds.
I tested for JS with Node 4.0.0 if anyone is interested. Declaring outside the loop resulted in a ~.5 ms performance improvement on average over 1000 trials with 100 million loop iterations per trial. So I'm gonna say go ahead and write it in the most readable / maintainable way which is B, imo. I would put my code in a fiddle, but I used the performance-now Node module. Here's the code:
var now = require("../node_modules/performance-now")
// declare vars inside loop
function varInside(){
for(var i = 0; i < 100000000; i++){
var temp = i;
var temp2 = i + 1;
var temp3 = i + 2;
}
}
// declare vars outside loop
function varOutside(){
var temp;
var temp2;
var temp3;
for(var i = 0; i < 100000000; i++){
temp = i
temp2 = i + 1
temp3 = i + 2
}
}
// for computing average execution times
var insideAvg = 0;
var outsideAvg = 0;
// run varInside a million times and average execution times
for(var i = 0; i < 1000; i++){
var start = now()
varInside()
var end = now()
insideAvg = (insideAvg + (end-start)) / 2
}
// run varOutside a million times and average execution times
for(var i = 0; i < 1000; i++){
var start = now()
varOutside()
var end = now()
outsideAvg = (outsideAvg + (end-start)) / 2
}
console.log('declared inside loop', insideAvg)
console.log('declared outside loop', outsideAvg)
A) is a safe bet than B).........Imagine if you are initializing structure in loop rather than 'int' or 'float' then what?
like
typedef struct loop_example{
JXTZ hi; // where JXTZ could be another type...say closed source lib
// you include in Makefile
}loop_example_struct;
//then....
int j = 0; // declare here or face c99 error if in loop - depends on compiler setting
for ( ;j++; )
{
loop_example loop_object; // guess the result in memory heap?
}
You are certainly bound to face problems with memory leaks!. Hence I believe 'A' is safer bet while 'B' is vulnerable to memory accumulation esp working close source libraries.You can check usinng 'Valgrind' Tool on Linux specifically sub tool 'Helgrind'.
It's an interesting question. From my experience there is an ultimate question to consider when you debate this matter for a code:
Is there any reason why the variable would need to be global?
It makes sense to only declare the variable once, globally, as opposed to many times locally, because it is better for organizing the code and requires less lines of code. However, if it only needs to be declared locally within one method, I would initialize it in that method so it is clear that the variable is exclusively relevant to that method. Be careful not to call this variable outside the method in which it is initialized if you choose the latter option--your code won't know what you're talking about and will report an error.
Also, as a side note, don't duplicate local variable names between different methods even if their purposes are near-identical; it just gets confusing.
this is the better form
double intermediateResult;
int i = byte.MinValue;
for(; i < 1000; i++)
{
intermediateResult = i;
System.out.println(intermediateResult);
}
1) in this way declared once time both variable, and not each for cycle.
2) the assignment it's fatser thean all other option.
3) So the bestpractice rule is any declaration outside the iteration for.
Tried the same thing in Go, and compared the compiler output using go tool compile -S with go 1.9.4
Zero difference, as per the assembler output.
I use (A) when I want to see the contents of the variable after exiting the loop. It only matters for debugging. I use (B) when I want the code more compact, since it saves one line of code.
Even if I know my compiler is smart enough, I won't like to rely on it, and will use the a) variant.
The b) variant makes sense to me only if you desperately need to make the intermediateResult unavailable after the loop body. But I can't imagine such desperate situation, anyway....
EDIT: Jon Skeet made a very good point, showing that variable declaration inside a loop can make an actual semantic difference.
Consider the following two ways of writing a loop in Java to see if a list contains a given value:
Style 1
boolean found = false;
for(int i = 0; i < list.length && !found; i++)
{
if(list[i] == testVal)
found = true;
}
Style 2
boolean found = false;
for(int i = 0; i < list.length && !found; i++)
{
found = (list[i] == testVal);
}
The two are equivalent, but I always use style 1 because 1) I find it more readable, and 2) I am assuming that reassigning found to false hundreds of times feels like it would take more time. I am wondering: is this second assumption true?
Nitpicker's corner
I am well aware that this is a case of premature optimization. That doesn't mean that it isn't something that is useful to know.
I don't care which style you think is more readable. I am only interested in whether one has a performance penalty compared to the other.
I know that style 1 has the advantage of allowing you to also put a break; statement in the if block, but I don't care. Again, this question is about performance, not style.
Well, just write a micro benchmark:
import java.util.*;
public class Test {
private static int[] list = new int[] {1, 2, 3, 4, 5, 6, 7, 8, 9} ;
private static int testVal = 6;
public static boolean version1() {
boolean found = false;
for(int i = 0; i < list.length && !found; i++)
{
if(list[i] == testVal)
found = true;
}
return found;
}
public static boolean version2() {
boolean found = false;
for(int i = 0; i < list.length && !found; i++)
{
found = (list[i] == testVal);
}
return found;
}
public static void main(String[] args) {
// warm up
for (int i=0; i<100000000; i++) {
version1();
version2();
}
long time = System.currentTimeMillis();
for (int i=0; i<100000000; i++) {
version1();
}
System.out.println("Version1:" + (System.currentTimeMillis() - time));
time = System.currentTimeMillis();
for (int i=0; i#lt;100000000; i++) {
version2();
}
System.out.println("Version2:" + (System.currentTimeMillis() - time));
}
}
On my machine version1 seems to be a little bit faster:
Version1:5236
Version2:5477
(But that's 0.2 seconds on a 100 million iterations. I wouldn't care about this.)
If you look at the generated bytecode there are two more instructions in version2 which probably cause the longer execution time:
public static boolean version1();
Code:
0: iconst_0
1: istore_0
2: iconst_0
3: istore_1
4: iload_1
5: getstatic #2; //Field list:[I
8: arraylength
9: if_icmpge 35
12: iload_0
13: ifne 35
16: getstatic #2; //Field list:[I
19: iload_1
20: iaload
21: getstatic #3; //Field testVal:I
24: if_icmpne 29
27: iconst_1
28: istore_0
29: iinc 1, 1
32: goto 4
35: iload_0
36: ireturn
public static boolean version2();
Code:
0: iconst_0
1: istore_0
2: iconst_0
3: istore_1
4: iload_1
5: getstatic #2; //Field list:[I
8: arraylength
9: if_icmpge 39
12: iload_0
13: ifne 39
16: getstatic #2; //Field list:[I
19: iload_1
20: iaload
21: getstatic #3; //Field testVal:I
24: if_icmpne 31
27: iconst_1
28: goto 32
31: iconst_0
32: istore_0
33: iinc 1, 1
36: goto 4
39: iload_0
40: ireturn
Comment about nitpicks corner:
If you're really concerned with absolute performance, putting a break in and removing the "&& !found" will give you theoretically better performance on #1. Two less binary ops to worry about every iteration.
If you wanted to get really anal about optimization without using breaks then
boolean notFound = true;
for(int i = 0; notFound && i < list.length; i++)
{
if(list[i] == testVal)
notFound = false;
}
will run faster in the average case than the existing option #1.
And of course it's personal preference, but I prefer to never put any extra evaluations inside the head of a for loop. I find it can cause confusion while reading code, because it's easy to miss. If I can't get the desired behavior using break/continues I will use a while or do/while loop instead.
Actually, the "if" will slow your program down more than assignment due to the pipeline.
It depends on what compiler you use since different compilers might do different optimizations.
I believe style 2 is ever-so-slightly faster - say 1 clock cycle or so.
I'd rewrite it into the following, though, if I were tackling it:
for(i=0; i<list.length && list[i]!=testval; i++);
boolean found = (i!=list.length);
It seems to me that if you expect your value to be found before the end of the list, you'd be better off with #2 - as it short circuits the check with !found in the loop conditional. Assuming you put a break in, the 1st option (the only sensible thing, IMO), then pseudo assembly would look something like:
Option 1:
start:
CMP i, list.length
JE end
CMP list[i], testval
JE equal
JMP start
equal:
MOV true, found
end:
Option 2:
start:
CMP i, list.length
JE end
CMP true, found
JE end
CMP list[i], testval
JE equal
JNE notequal
equal:
MOV true, found
JMP start
notequal:
MOV false, found
JMP start
end:
I'd say Option 1 is superior here, as it's about 1/3rd less instructions. Of course, this is without optimizations - but that'd be compiler and situation specific (what is found doing after this? can we just optimize it away all together?).
Here is another style
for(int i = 0; i < list.length; i++)
{
if(list[i] == testVal)
return true;
}
return false;
I think both alternatives leave something to be desired, from a performance point of view.
Think about how many tests (which are almost always jumps) you do per iteration, and try to minimize the amount.
The solution by Matt, to return out when the answer is found, reduces the number of tests from three (loop iterator, found-test in loop, actual comparison) to two. Doing the "found"-test essentially twice is wasteful.
I'm not sure if the classic, but somewhat obfuscating, trick of looping backwards is a win in Java, and not hot enough at reading JVM code to figure it out right now, either.
I would say that in 98% of systems, it does not matter. The difference, if there is any, is hardly noticeable unless that loop is the main portion of code and is running a mindnumbing number of times.
Edit: That is ofcourse assuming that it is not being already optimized by the compiler.
Any decent compiler would keep found in a register for a duration of the loop and so the cost is absolutely negligible.
If the second style is done without a branch then it would be preferable, since the CPU's pipeline will not get disrupted as much ... but that depends on how the compiler uses the instruction set.
This will only be measurable in code which is extremely performance-sensitive (simulators, emulators, video encoding software, etc.) in which case you probably want to manually inspect the generated code anyway to make sure that the compiler actually generates sensible code.
To be sure, you should compile both versions (say with latest compiler from Sun) and examine the generated bytecode with an appropriate tool... That's the only reliable way to know for sure, everything else is wild guess.
boolean found = false;
for(int i = 0; i < list.length && !found; i++)
{
if(list[i] == testVal)
found = true;
}
I don't see a break statement in the block.
Other than that, I prefer this style. It improves readability and thereby the chance that a maintainer mis-reading and mis-fixing it.