SyntaxHighlighter

Wednesday, October 14, 2015

assignment-with-use or inline assignment

I am still astonished by some persistent folklore and "rules of thumb" that find their origin in a so ancient time that most of people forget why they follow this rule.

Recently, I have heard one of those rules of thumb:
If you want to keep object references into the registers you need to perform inline assignment in a if statement for example:

which is claimed to be more efficient than:
To back this statement, the JDK source code is used as best practices to be followed. More precisely in LongAdder class:

Nice example of inline assignment written by Doug Lea (or at least approved by him). Why not doing like him, after all, he knows what is doing, right?

I have searched for explanation about this style, and found this thread where Rémi Forax found this code "not very java-ish". Aurélien Gonnay noticed also a difference of style between JDK code and JSR one on the method sumThenReset.

So let's simplify this code but keeping the inline assignement style. I get a similar example from the previous thread with Rémi Forax:

A more Java-ish code will be the following:
Firstly, we compare the generated byte code for inline assignments:
with no inline assignments:

Here a diff as summary:
3,4c3,4        
<  4: dup      
<  5: astore_1 
---            
>  4: astore_1 
>  5: aload_1  
8,9c8,9        
< 11: dup      
< 12: istore_2 
---            
> 11: istore_2 
> 12: iload_2  
18,19c18,19    
< 25: dup      
< 26: astore_3 
---            
> 25: astore_3 
> 26: aload_3  
Differences are only spotted at lines 4, 11 and 25. instead of dup + store for inline assignment we get store + load which semantically is the same. But is it more optimized with the first form ? Let's go check the assembly:
Compared to no inline assignments:

Here are the commands to diff without the addresses:
sed -e "s/0x[0-9a-f]*/0/g" inlineAssign1.txt > inlineAssign1_sed.txt

sed -e "s/0x[0-9a-f]*/0/g" inlineAssign2.txt > inlineAssign2_sed.txt

diff -w inlineAssign1_sed.txt inlineAssign2_sed.txt
1c1
<   # {method} {0} 'bench_c' '()V' in 'main/java/TestJIT'
---
>   # {method} {0} 'bench_java' '()V' in 'main/java/TestJIT'
7c7
<                                          ; - main.java.TestJIT::bench_c@-1 (line 16)
---
>                                          ; - main.java.TestJIT::bench_java@-1 (line 24)
10c10
<                                          ; - main.java.TestJIT::bench_c@1 (line 16)
---
>                                          ; - main.java.TestJIT::bench_java@1 (line 24)
13c13
<                                          ; - main.java.TestJIT::bench_c@10 (line 16)
---
>                                          ; - main.java.TestJIT::bench_java@10 (line 26)
17c17
<                                          ; - main.java.TestJIT::bench_c@13 (line 16)
---
>                                          ; - main.java.TestJIT::bench_java@13 (line 27)
23c23
<                                          ; - main.java.TestJIT::bench_c@23 (line 17)
---
>                                          ; - main.java.TestJIT::bench_java@23 (line 28)
29c29
<                                          ; - main.java.TestJIT::bench_c@24 (line 17)
---
>                                          ; - main.java.TestJIT::bench_java@24 (line 28)
33c33
<                                          ; - main.java.TestJIT::bench_c@27 (line 17)
---
>                                          ; - main.java.TestJIT::bench_java@27 (line 29)


You can check by yourself, line by line: each instruction is exactly the same on both version, only the addresses are different and the comment because, in my test, the method name is not the same.
Conclusion? Inline assignments have no value in term of performance, JIT is sufficiently smart to handle this.

Note: I have used JDK 1.8.0_40 for this article.

Thanks to Aurélien Gonnay for the review.