2016年8月16日 星期二

Performance difference of using String.intern(), Compare by Reference & Compare by Value - Part 2 / 2


Continued from "Performance difference of using String.intern(), Compare by Reference & Compare by Value - Part 1 / 2", let’s go further on the captioned topic.

How about the cost of applying String.intern()?

Let’s take a look of the following sample code:

public class Test3 {
    
     public void instantiateNoIntern(){
         long start = System.nanoTime();
         String a = "ABCDEFGHIJKLMNOPQRSTUV";
         String b = new String("ABCDEFGHIJKLMNOPQRSTUV");
         long end = System.nanoTime();
         System.out.println("Elapsed Time (ns): " + (end - start) );
     }

     public void instantiateWithIntern(){
         long start = System.nanoTime();
         String a = "ABCDEFGHIJKLMNOPQRSTUV";
         String b = new String("ABCDEFGHIJKLMNOPQRSTUV").intern();
         long end = System.nanoTime();
         System.out.println("Elapsed Time (ns): " + (end - start) );
     }
    
     public static void main(String[] args){
        
         Test3 test = new Test3();
         System.out.println("Instantiation cost NO String.intern() : ");
         for (int i = 10; i > 0 ; i--){
              test.instantiateNoIntern();
         }
        
         System.out.println("");
         System.out.println("Instantiation cost with String.intern() : ");
         for (int i = 10; i > 0 ; i--){
              test.instantiateWithIntern();
         }
     }
    
}



The execution result is shown below:

Instantiation cost without String.intern() :
Elapsed Time (ns): 3622
Elapsed Time (ns): 603
Elapsed Time (ns): 302
Elapsed Time (ns): 302
Elapsed Time (ns): 302
Elapsed Time (ns): 302
Elapsed Time (ns): 0
Elapsed Time (ns): 302
Elapsed Time (ns): 301
Elapsed Time (ns): 301

Instantiation cost with String.intern() :
Elapsed Time (ns): 2113
Elapsed Time (ns): 604
Elapsed Time (ns): 604
Elapsed Time (ns): 302
Elapsed Time (ns): 905
Elapsed Time (ns): 604
Elapsed Time (ns): 604
Elapsed Time (ns): 604
Elapsed Time (ns): 604
Elapsed Time (ns): 603

 Here is the result summary:


Total Time (nanosecond)
Without applying String.intern()
6,337
Has been applying String.intern()
7,547
Difference for 100 operations
1,210


From the result above, we can see that the cost for applying intern() is very low in actual which means intern() is still worth for applying as the time saved by alternatively using Compare by Reference is significant.


Let’s integrate this result with the tables before to get an overall view:


Total Time (nanosecond)
Result Correctness
Comparison operation
Instantiation
Comparison Operation + Instantiation
Before using String.intern()
By Reference
2,416
6,337
8,753
Incorrect
By Value
49,203
6,337
55,540
Correct
After using String.intern()
By Reference
2,415
7,547
9,962
Correct
By Value
6,943
7,547
14,490
Correct
Remarks
By Reference:  ==
By Value:          .equals()


Hope this article is interesting to you and hope you enjoy it!

Performance difference of using String.intern(), Compare by Reference & Compare by Value - Part 1 / 2


When we learn about String comparison on Java, most of us are told to use .equals() [i.e. value comparison / content comparison ] instead of using == [i.e. reference comparison].

It is true and safe approach as String is immutable object, i.e. each newly instantiated String object are located at different memory location if we use String str1 = new String() but not String str1 = “Test1”; even their values are the same. 

However, in Java, there is a String constant pool in the JVM in actual. Details could be referred to Javin’s blog:
https://javarevisited.blogspot.com/2015/12/when-to-use-intern-method-of-string-in-java.html


As we all know compare strings by reference (i.e. == ) is much faster than comparing by values (i.e. .equals() ), how can we make use of the benefits from reference comparison for String?
The answer is make use of String.intern().


At first, let’s take a glance on the performance between value comparison and reference comparison of String by below sample code:

public class Test {
    
     private static final int iterateTimes = 10;
     private static final int loopingTimes = 10;
    
     private long start, end;
    
     private static final String a = "ABCDEFGHIJKLMNOPQRSTUV";
     private static final String b = new String("ABCDEFGHIJKLMNOPQRSTUV");
    
     public void iterateOnLoop(){

         System.out.println("Reference Comparison: ");
         for (int i = iterateTimes ; i > 0 ; i--){
              loopByReferenceComparison();
         }
         System.out.println("");
         System.out.println("Value Comparison: ");
         for (int i = iterateTimes ; i > 0 ; i--){
              loopByValueComparison();
         }
        
     }
    
     public void loopByReferenceComparison(){
         start = System.nanoTime();
         for (int i = loopingTimes ; i > 0; i--){
              if ( a == b){
                  // TODO Print "true" here:
                  // System.out.println("True");
              }
              else{
                  // TODO Print "false" here:
                  // System.out.println("False");
              }
         }
         end = System.nanoTime();
         System.out.println("Elapsed Time (ns): " + (end - start) );
     }
    
     public void loopByValueComparison(){
         start = System.nanoTime();
         for (int i = loopingTimes ; i > 0; i--){
              if (a.equals(b)) {
                  // TODO Print "true" here:
                  // System.out.println("True");
              }
              else{
                  // TODO Print "false" here:
                  // System.out.println("True");
              }
         }
         end = System.nanoTime();
         System.out.println("Elapsed Time (ns): " + (end - start) );
     }

     public static void main(String[] args){
        
          Test test = new Test();
          test.iterateOnLoop();
        
     }
}





Description


The above sample has instantiated 2 String objects “a” and “b” with the same value. Both String objects are compared by 2 approaches: value comparison, reference comparison. The comparison has been repeated 10 times for each batch. And the 10 sample result has been retrieved repeatedly for each batch.


Result
Let’s take a look of the results between value comparison and reference comparison as below:
Reference Comparison:
Elapsed Time (ns): 906
Elapsed Time (ns): 302
Elapsed Time (ns): 0
Elapsed Time (ns): 302
Elapsed Time (ns): 301
Elapsed Time (ns): 0
Elapsed Time (ns): 0
Elapsed Time (ns): 302
Elapsed Time (ns): 302
Elapsed Time (ns): 0

Value Comparison:
Elapsed Time (ns): 6640
Elapsed Time (ns): 3622
Elapsed Time (ns): 3622
Elapsed Time (ns): 3622
Elapsed Time (ns): 3622
Elapsed Time (ns): 3622
Elapsed Time (ns): 3924
Elapsed Time (ns): 20526
Elapsed Time (ns): 4226
Elapsed Time (ns): 4226




The 10 sample results are summarized as below:

Total Time (nanosecond)
Compare by reference
2,416
Compare by value
49,203



Compare by reference” approach is nearly 20 times faster than “Compare by value”!! [ 49,203 / 2,416 = 20.37 ]


However, the comparison result of using “Compare by reference” above is definitely incorrect if you have enabled / implemented the TODO section of the above sample code. You can also refer to below sample on the comparison result:
public class Test2 {

     public static void main(String[] args){
         String a = "ABCDEFGHIJKLMNOPQRSTUV";
         String b = new String("ABCDEFGHIJKLMNOPQRSTUV");
        
         // Case 1: Compare by Reference
         System.out.print("Compare by Reference:\t\t");
         if ( a == b) {
              // TODO Print "true" here:
              System.out.println("True");
         } else {
              // TODO Print "false" here
              System.out.println("False");
         }
        
         // Case 2: Compare by Value
         System.out.print("Compare by Value:\t\t");
         if (a.equals(b)) {
              // TODO Print "true" here
              System.out.println("True");
         } else {
              // TODO Print "false" here
              System.out.println("False");
         }
     }
}





Result:
Compare by Reference:      False
Compare by Value:          True

Here we come the question as below:
Q: how can we benefit from "Compare by reference" for String object but still getting the correct result?
Ans: We can make use of String.intern()
 

Adjustment and Tuning


Let’s make a minor change to below code snippet for the declaration of String b :
private static final String b = new String("ABCDEFGHIJKLMNOPQRSTUV").intern();
    

After this change, if the String pool contains a String object which the equals() returns true, the String object from the pool will be returned. It means the String b will be assigned to the same object reference of String a in the above example. So the comparison performance could be improved by using Reference Comparison instead of Value Comparison along with guaranteed correct result.


Let’s see the behavior and performance after fine tuning with above approach.
Comparison result (behavior):
Compare by Reference:      True
Compare by Value:          True

Comparison performance:
Reference Comparison:
Elapsed Time (ns): 1208
Elapsed Time (ns): 302
Elapsed Time (ns): 302
Elapsed Time (ns): 0
Elapsed Time (ns): 0
Elapsed Time (ns): 0
Elapsed Time (ns): 0
Elapsed Time (ns): 302
Elapsed Time (ns): 0
Elapsed Time (ns): 301

Value Comparison:
Elapsed Time (ns): 3018
Elapsed Time (ns): 604
Elapsed Time (ns): 603
Elapsed Time (ns): 302
Elapsed Time (ns): 302
Elapsed Time (ns): 604
Elapsed Time (ns): 604
Elapsed Time (ns): 302
Elapsed Time (ns): 302
Elapsed Time (ns): 302




Summary of Comparison Performance:

Total Time (nanosecond)
Compare by reference
2,415
Compare by value
6,943


Here we got an interesting result. Let combine the summaries of before and after using String.intern() to below table for getting an overall picture:

Total Time (nanosecond)
Comparison Correctness
Before using String.intern()
Compare by reference
2,416
Incorrect
Compare by value
49,203
Correct
After using String.intern()
Compare by reference
2,415
Correct
Compare by value
6,943
Correct

The Compare by Reference result is now as expected and the performance is not affected significant. However, there is one more interesting outcome. The Compare by Value performance is also improved significantly!

In conclusion, String.intern() can significantly help on improving the comparison operation.

How about the cost of applying String.intern()? You may also be interested in the following article:  
Performance difference of using String.intern(), Compare by Reference & Compare by Value - Part 2 / 2