Improve the performance of Java code

Source: Internet
Author: User
Tags contains integer variables return string version variable
Performance

Tail-recursive conversions can speed up the application, but not all jvms do the conversion

Many algorithms use the tail-recursion method to express themselves to be particularly concise. The compiler automatically converts this method into a loop to improve the performance of the program. In the Java language Specification, however, there is no requirement to make this transition, so not all Java virtual machines (JVMS) do this. This means that the use of tail-recursive representations in the Java language can result in huge memory footprint, which is not what we expect. Eric Allen explains in this article that dynamic compilation will preserve the semantics of the language, while static compilation is not usually. He explains why this is an important issue and provides a piece of code to help you determine whether your Just-in-time (JIT) compiler will be able to do tail-recursive code conversion while maintaining language semantics.

  Tail recursion and its transformation

Quite a few programs contain loops, which account for a large portion of the total running time of the program. These loops often have to update more than one variable, and each variable's update often relies on the value of other variables.

If you think of an iteration as a tail-recursive function, you can think of these variables as parameters of a function. A simple reminder: If the return value of a call is returned immediately as the value of the calling function, then the recursive call is the tail recursion; tail recursion does not have to remember the context in which the function is invoked when invoked. &NBSP

Due to this feature, there is a good correspondence between the tail recursive function and the loop: each recursive call can be considered simply as a multiple iteration of a loop. However, because all variable parameter values are passed to the recursive call at once, it is easier to get updated values in tail recursion than in the loop. Also, difficult to use  break  statements are often replaced by simple returns of functions.  &NBSP

But in  Java  programming, it is inefficient to represent iterations in this way, because a large number of recursive calls have the risk of causing a stack overflow.  &NBSP

Solution is simpler: because the tail recursive function is actually just a simpler way to write loops, let the compiler automatically convert them to a circular form. This allows you to take advantage of both forms.  &NBSP

However, although everyone knows how to automatically convert a tail recursive function to a simple loop,java  specification does not require this conversion. The reason for not doing this is probably this: typically in object-oriented languages, this conversion cannot be done statically. Conversely, this conversion from the tail recursive function to a simple loop must be performed dynamically by the  JIT  compiler.  &NBSP

To understand why this is the case, consider one of the following failed attempts: On the  Integers  set, multiply the elements in  Iterator .  &NBSP

throws an exception at run time because there is an error in the following program. But, as already argued in many of the previous articles in this column, the exact exception thrown by a program (as well as a great error type identifier) is not helpful in finding where the error is hidden in the program, and we do not want the compiler to alter the program in this way so that the compiled result code throws a different exception.  &NBSP

manifest  1.  a failed attempt to multiply elements in  Iterator  of a  Integer  set  

IMPORT JAVA.UTIL.ITERATOR; 

public class example { 

  public int product (iterator i)  { 
    return  producthelp (i, 0);  
  } 

  int producthelp (Iterator  i, int accumulator)  { 
    if  (I.hasnext ())  { 
      return producthelp (i, accumulator *  (Integer) I.next ()). Intvalue ());  
    } 
    else { 
      return accumulator; 
    } 
  }&NBSP

Note the error in the product method. The product method calls Producthelp by assigning the accumulator to 0. It should have a value of 1. Otherwise, calling product on any instance of the class Example produces a value of 0, regardless of iterator value.

Suppose this error is finally corrected, but at the same time a subclass of class Example is created, as shown in Listing 2:

  Listing 2. Trying to catch an incorrect call like Listing 1

Import java.util.*;

Class Example {

public int product (iterator i) {
Return Producthelp (i, 1);
}

int Producthelp (iterator i, int accumulator) {
if (I.hasnext ()) {
Return Producthelp (i, Accumulator * ((Integer) I.next ()). Intvalue ());
}
else {
return accumulator;
}
}
}

And, in a separate file:

Import java.util.*;

public class Example2 extends Example {
int Producthelp (iterator i, int accumulator) {
if (Accumulator < 1) {
throw new RuntimeException ("accumulator to Producthelp must is >= 1");
}
else {
Return Super.producthelp (i, accumulator);
}
}

public static void Main (string[] args) {
LinkedList L = new LinkedList ();
L.add (new Integer (0));
New Example2 (). Product (L.listiterator ());
}
}

The overridden Producthelp method in class Example2 attempts to catch incorrect calls to Producthelp by throwing a Run-time exception when Accumulator is less than "1". Unfortunately, this will introduce a new error. If iterator contains any 0-valued instances, the producthelp crashes on its own recursive call.

Notice now that in the main method of class Example2, you create an instance of Example2 and call its product method. Because the iterator passed to this method contains a 0, the program crashes.

However, you can see that the producthelp of the class Example is strictly tail-recursive. Suppose a static compiler wants to convert the body of this method into a loop, as shown in Listing 3:

  Listing 3. Static compilation does not optimize an example of a tail call

int Producthelp (iterator i, int accumulator) {
while (I.hasnext ()) {
Accumulator *= ((Integer) I.next ()). Intvalue ();
}
return accumulator;
}

So, the initial call to Producthelp, the result is a call to the superclass method. The Super method calculates the result by simply looping through the iterator. Does not throw any exceptions.

Compiling this code with two different static compilers results in one that throws an exception and the other does not, and think about how confusing that is.

  Does your JIT make this conversion?

So, as the example in Listing 3 shows, we can't expect the static compiler to perform a tail-recursive conversion of Java code while maintaining language semantics. Instead, we must rely on the JIT for dynamic compilation. The JIT will not do this conversion depends on the JVM.

One way to determine whether your JIT will convert tail recursion is to compile and run the following small test class:

  Listing 4. To determine whether your JIT can convert tail recursion

public class Tailrecursiontest {

private static int loop (int i) {
return loop (i);
}

public static void Main (string[] args) {
Loop (0);
}
}

Let's consider the loop method for this class. This method simply makes recursive calls to itself as long as possible. Because it never returns, nor does it affect any external variables in any way, so replacing its code body as shown in Listing 5 preserves the semantics of the program.

Listing 5. A dynamic transformation

public class tailrecursiontest { 

  private static  int loop (int i)  { 
    while  (true)  { 
&NBSP;&NBSP;&NBSP;&NBSP;}&NBSP
  } 

  public static void  Main (String[] args)  { 
    loop (0);  
  } 
}  

And, in fact, this is the transformation made by a sufficiently sophisticated compiler.

If your JIT compiler converts tail recursive calls to iterations, the program will run indefinitely. It requires a small amount of memory and does not increase over time.

On the other hand, if the JIT does not do this conversion, the program will quickly run out of stack space and report a stack overflow error.

I ran this program on two Java SDK, and the result was amazing. When run on SUN's Hotspot JVM (version 1.3), it was found that Hotspot did not perform this conversion. By default, when running on my machine, the stack space is depleted in less than a second.

On the other hand, the program is running on the IBM JVM (version 1.3) without any problems, indicating that IBM's JVM transforms the code in this way.

  Summary

Remember: We cannot hope that our code will always run on the JVM that will convert the tail recursive call. Therefore, to ensure that your program has the proper performance on all JVMs, you should always try to write the code that most naturally conforms to the end-recursive pattern in an iterative style.

But note: As our example demonstrates, it is easy to introduce errors in this way when translating code, whether it is done manually or by software.

In fact, found such an article from the Internet is not difficult, I hope that you encounter problems can learn how to deal with

The process of learning is the process of learning how to learn, isn't it?



Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.