In-depth understanding of Java Virtual Machines (14) How to properly leverage JVM methods inline

Source: Internet
Author: User
Tags stack trace

In IntelliJ idea, ctrl+alt+m is used to split the method. Selecting a piece of code, knocking down this combination, is very simple. Eclipse also uses a similar shortcut key, using Alt+shift+m. I hate the long way, I think it's too long to mention this method:

public void Processonendofday (contract c) {if (Dateutils.adddays (c.getcreated (), 7). Before (new Date ())) { Priorityhandling (c, Outdated_fee); notifyoutdated (c); Log.info ("outdated: {}", c);} else {if (sendnotifications) {notifypending (c);} Log.debug ("Pending {}", c);}}

First, it has a condition to judge that readability is poor. Regardless of how it is implemented, it is the key to what it does. Let's split it up first:

public void Processonendofday (contract c) {if (Isoutdate (c)) {priorityhandling (c, Outdated_fee); notifyoutdated (c); Log.info ("outdated: {}", c);} else {if (sendnotifications) {notifypending (c);} Log.debug ("Pending {}", c);}} Private Boolean isoutdate (contract c) {return dateutils.adddays (c.getcreated (), 7). Before (new Date ());}

Obviously, this method should not be put here:

public void Processonendofday (contract c) {if (C.isoutdate ()) {priorityhandling (c, Outdated_fee); notifyoutdated (c); Log.info ("outdated: {}", c);} else {if (sendnotifications) {notifypending (c);} Log.debug ("Pending {}", c);}}

Notice what's different? My IDE changed the Isoutdated method to an example of contract, which is just decent. But I'm still upset. This method is too miscellaneous to do. A branch handles business-related logic priorityhandling, as well as sending system notifications and logging. The other branch then makes a system notification based on the condition of the judgment and logs the log. We first split the processing of overdue contracts into a separate method.

public void Processonendofday (contract c) {if (C.isoutdate ()) {handleoutdated (c);} else {if (sendnotifications) { Notifypending (c);} Log.debug ("Pending {}", c);}} private void handleoutdated (contract c) {priorityhandling (c, Outdated_fee); notifyoutdated (c); Log.info ("outdated: {}" , c);}

Some people will think it's good enough, but I think the two branches are not very symmetrical. The Handleoutdated method level is higher, while the Else branch is more detail-biased. The software should be legible, so don't mix the code between the different tiers. So I will be more satisfied:

public void Processonendofday (contract c) {if (C.isoutdate ()) {handleoutdated (c);} else {stillpending (c);}} private void stillpending (contract c) {if (sendnotifications) {notifypending (c);} Log.debug ("Pending {}", c);} private void handleoutdated (contract c) {priorityhandling (c, Outdated_fee); notifyoutdated (c); Log.info ("outdated: {}" , c);}

This example looks a little bit, but in fact I want to prove another thing. Although it is less common now, some developers do not dare to split the method, fearing that this will affect the efficiency of the operation. They didn't know that the JVM was actually a great software (it actually dumped a few streets in the Java language), and it built many very surprising runtime optimizations. First of allShort method is more advantageous to JVM inference。 The process is more pronounced,a shorter scope, and the side effects are more pronounced. If it is a long method the JVM may be kneeling directly. A second reason is more important:

Method inline

If the JVM detects a few small methods that are frequently executed, it replaces the call of the method with the method body itself. For example, the following:

private int add4 (int x1, int x2, int x3, int x4) {return add2 (x1, x2) + ADD2 (x3, x4);} private int add2 (int x1, int x2) {return x1 + x2;}

It is certain that the JVM will remove the Add2 method after running for a period of time and translate your code into:

private int add4 (int x1, int x2, int x3, int x4) {return x1 + x2 + x3 + x4;}

Note that this is about the JVM, not the compiler. Javac are more conservative when generating bytecode, and these jobs are thrown to the JVM. It turns out that such design decisions are very sensible:

The JVM more clearly runs the target environment, CPU, memory, and architecture, which can be more aggressively optimized. The JVM can discover the characteristics of your code runtime, such as which method is executed frequently, which virtual method has only one implementation, and so on. The. Class compiled by the old compiler can get faster running on the new version of the JVM. Update the JVM and recompile the source code, you are certainly more inclined to the latter.

Let's take a test of these assumptions. I wrote a small program that has the worst realization of the principle of division. The add128 method requires 128 parameters and calls two ADD64 methods--one and two halves at a time. ADD64 is similar, but it is called two times add32. You guessed it, and finally it was done by the Add2 method. Some numbers I have omitted, lest the light blinded your eyes:

public class Concreteadder {public int add128 (int x1, int x2, int x3, int x4, ..., int x127, int x128) {RE  Turn add64 (x1, x2, x3, x4, ..., x63, x64) + ADD64 (x65, x66, x67, x68, ..., x127, x128); } private int add64 (int x1, int x2, int x3, int x4, ..., int x63, int x64) {return add32 (x1, x2, X3, X4,...) .  More ..., x31, x32) + add32 (x33, x34, x35, x36, ..., x63, x64); } private int add32 (int x1, int x2, int x3, int x4, ..., int x31, int x32) {return add16 (x1, x2, X3, X4,...) .  More ..., x15, x16) + add16 (x17, x18, x19, x20, ..., x31, x32);  } private int Add16 (int x1, int x2, int x3, int x4, ..., int x15, int x16) {return Add8 (x1, x2, X3, X4, X5,  X6, X7, x8) + ADD8 (x9, X10, X11, X12, X13, x14, x15, x16); } private int Add8 (int x1, int x2, int x3, int x4, int x5, int x6, int x7, int x8) {return add4 (x1, x2, x3, x4) + ad  D4 (X5, X6, X7, x8); } private int add4 (int x1, int x2, int x3, int x4) {return add2 (x1, x2) + ADD2 (x3, x4);  } private int add2 (int x1, int x2) {return x1 + x2;  }}

It is not difficult to see that the call to the add128 method resulted in a total of 127 method invocations. Too much. As a reference, here is a simple and straightforward implementation version:

public class Inlineadder {public     int add128n (int x1, int x2, int x3, int x4, ..., int x127, int x128) {        Return x1 + x2 + x3 + x4 + ... more ... + x127 + x128;    } }

Finally, another implementation version that uses the abstract class and inheritance. The cost of 127 virtual method calls is very large. These methods need to be distributed dynamically and therefore require higher, so they cannot be inline.

Public abstract class Adder {public   abstract int add128 (int x1, int x2, int x3, int x4, ... more ..., int x127, int x );   public abstract int Add64 (int x1, int x2, int x3, int x4, ..., int x63, int x64);   public abstract int add32 (int x1, int x2, int x3, int x4, ..., int x31, int x32);   public abstract int Add16 (int x1, int x2, int x3, int x4, ..., int x15, int x16);   public abstract int Add8 (int x1, int x2, int x3, int x4, int x5, int x6, int x7, int x8);   public abstract int Add4 (int x1, int x2, int x3, int x4);   

There is also an implementation:

public class Virtualadder extends Adder {@Override public int add128 (int x1, int x2, int x3, int x4, ... more ..., int x128) {return add64 (x1, x2, x3, x4, ..., x63, x64) + ADD64 (x65, x66, x67, x68, ... more ..., x127, x1  28); } @Override public int add64 (int x1, int x2, int x3, int x4, ..., int x63, int x64) {return add32 (x1, x2,  X3, x4, ... more ..., x31, x32) + add32 (x33, x34, x35, x36, ..., x63, x64); } @Override public int add32 (int x1, int x2, int x3, int x4, ..., int x32) {return add16 (x1, x2, X3, X4,. ..  More ..., x15, x16) + add16 (x17, x18, x19, x20, ..., x31, x32); } @Override public int add16 (int x1, int x2, int x3, int x4, ..., int x16) {return Add8 (x1, x2, X3, X4, X5  , X6, X7, x8) + ADD8 (x9, X10, X11, X12, X13, x14, x15, x16); } @Override public int add8 (int x1, int x2, int x3, int x4, int x5, int x6, int x7, int x8) {return add4 (x1, x2, x3 , x4) + add4 (X5, X6, X7, x8);  } @Override public int add4 (int x1, int x2, int x3, int x4) {return add2 (x1, x2) + ADD2 (x3, x4);  } @Override public int add2 (int x1, int x2) {return x1 + x2;  }}

Inspired by some enthusiastic readers of my other article on @cacheable load, I wrote a simple benchmark to compare the load of these two over-split concreteadder and Virtualadder. The results were surprising and somewhat confusing. I did the test on both machines (red and blue), and the same program differs from the second machine with more CPU cores and 64 bits:


Specific environmental information:


On a machine that looks slow, the JVM prefers to do method inline. Not only is the version of a simple private method call, but the version of the virtual method is the same. Why is that? Because the JVM discovers that Adder has only one subclass, which means that each abstract method has only one version. If you load another subclass (or more) at run time, you'll see that performance will go straight down because the inability to inline again. First of all, from the test,

The invocation of these methods is not overhead, there is no overhead at all!

Method calls (and documents added for readability) exist only in your source code and in the compiled bytecode, and they are completely erased at runtime (inline).

I don't quite understand the second result. It appears that high performance machine B runs a single method call faster, while the other two is slower. Perhaps it tends to delay the inline? The results are somewhat different, but the gap is not so big. Just like optimizing stack trace information Generation-if you are manually inline to optimize code performance, make the method bigger and more complex, then you are really wrong.

Ps:64bit machines may be slow to run because the JVM's inline requirements are longer.

The original article is from:

Http://www.javacodegeeks.com/2013/02/how-aggressive-is-method-inlining-in-jvm.html
Http://it.deepinmind.com/java/2014/03/01/JVM the method inline. html

In-depth understanding of Java Virtual Machines (14) How to properly leverage JVM methods inline

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.