Java: how much time does an empty loop use?
I am trying to test the speed of autoboxing and unboxing in Java, but when I try to compare it against an empty loop on a primitive, I noticed one curious thing. This snippet:
for (int j = 0; j < 10; j++) {
long t = System.currentTimeMillis();
for (int i = 0; i < 10000000; i++)
;
t = System.currentTimeMillis() - t;
System.out.print(t + " ");
}
Every time I run this, it returns the same result:
6 7 0 0 0 0 0 0 0 0
Why does the first two loops always take some time, then the rest just seem to be skipped by the system?
In this answer to this post, it is said that Just-In-Time compilation will be able to optimize this away. But if so, why the first two loop开发者_开发知识库s still took some time?
JIT triggers AFTER a certain piece of code has been executed many times.
The HotSpot JVM will try to identify "hot spots" in your code. Hot spots are pieces of your code that are executed many many times. To do this, the JVM will "count" the executions of various instructions, and when it determines a certain piece is executed frequently, it will trigger the JIT. (this is an approximation, but it's easy to understand explained this way).
The JIT (Just-In-Time) takes that piece of code, and tries to make it faster.
The techniques used by the JIT to make your code run faster are a lot, but the one that most commonly creates confusion are :
- It will try to determine if that piece of code uses variables that are not used anywhere else (useless variables), and remove them.
- If you acquire and release the same lock multiple times (like calling synchronized methods of the same object), it can acquire the lock once and do all the calls in a single synchronized block
- If you access members of an object that are not declare volatile, it can decide to optimize it (placing values in registers and similar), creating strange results in multi-threading code.
- It will inline methods, to avoid the cost of the call.
- It will translate bytecode to machine code.
- If the loop is completely useless, it could be completely removed.
So, the proper answer to your question is that an empty loop, after being JITed, takes no time to execute .. most probably is not there anymore.
Again, there are many other optimizations, but in my experience these are among those that have created most headaches.
Moreover, JIT is being improved in any new version of Java, and sometimes it is even a bit different depending on the platform (since it is to some extent platform specific). Optimizations done by the JIT are difficult to understand, because you cannot usually find them using javap and inspecting bytecode, even if in recent versions of Java some of these optimizations have been moved to the compiler directly (for example, since Java 6 the compiler is able to detect and warn about unused local variables and private methods).
If you are writing some loops to test something, it is usually good practice to have the loop inside a method, call the method a few times BEFORE timing it, to give it a "speed up" round, and then perform the timed loop.
This usually triggers the JIT in a simple program like yours, even if there is no guarantee that it will actually trigger (or that it even exists on a certain platform).
If you want to get paranoid about JIT or non JIT timing (I did): make a first round, timing each execution of the loop, and wait until the timing stabilize (for example, difference from the average less than 10%), then start with your "real" timing.
The JIT doesn't kick in on a chunk of code until it determines that there is some benefit to doing so. That means the first few passes through some code won't be JITed.
精彩评论