//java architect /
tion count (for the number of iterations
performed so far).
Note: The String and UTF_ 8 classes are
not used directly by the test, but compilation output still appears for them
because they’re used by the platform.
On the second line in Listing 2, you
can see that both tests are very slow.
This is because the first run of the code
includes the time to load each class. The
next line is much faster even though no
code tested is compiled at this stage.
Also note the following:
; ; At 1,000 and 5,000 iterations, direct
access to the fields is faster than
via getter/setter methods because
the getter and setter have not been
inlined or even optimized. Even so,
both are pretty fast.
; ; By 9,000 iterations, the getter is
optimized (because it is called twice
per loop), which gives a slight overall
improvement to performance.
; ; By 10,000 iterations, the setter has
been optimized. The extra time spent
including the optimized code means
the code briefly runs slower.
; ; Finally, both test classes are optimized:
; ; DFACaller uses direct access to the
fields, and GetSetCaller uses the
getter and setter. This is the point at
which the getter and setter are not
just optimized; they are also inlined.
; ; You can see that in the next
iterations, the test times are still
not optimal.
; ; After 13,000 iterations, the performance for each is as good as the final,
much longer test. We’ve reached
steady-state performance.
The important thing to note is that
the steady-state performance for
accessing fields directly or using getters and setters is basically the same
because the methods have (finally) been
inlined into GetSetCaller, meaning the
callable code in viaGetSet is doing exactly
the same work as the code in directCall
(which accesses the fields directly).
The JIT compilation is performed in
the background, and exactly when each
optimization is available for execution
varies from machine to machine and,
somewhat, from run to run.
LISTING 1 LISTING 1A LISTING 1B LISTING 2
public class Main {
COMMUNITY
private static double time TestRun(String desc, int runs, Callable<Double> callable)
throws Exception {
}
// Housekeeping method to provide nice uptime values for us
private static long uptime() {
// fudge factor
}
return ManagementFactory.getRuntimeMXBean().getUptime() + 15;
JAVA IN ACTION
Conclusion
In this article, we’ve shown you the very
tip of the JIT compilation iceberg. In particular, we haven’t addressed some very
important aspects of how to write good
benchmarks and how to use statistics to
ensure that the dynamic nature of the
platform isn’t fooling you.
The benchmark used here is very simple, and it isn’t suitable for a real benchmark. In Part 2, we plan to show you how
to handle a more realistic benchmark
and also delve deeper into the code that
the JIT compiler produces when it compiles your code. </article>
public static void main(String... args) throws Exception {
int iterations = 0;
for (int i : new int[]{ 100, 1000, 5000, 9000, 10000, 11000, 13000, 20000,
// NOTE: We return double (sum of values) from our test cases to
// prevent aggressive JIT compilation from eliminating the loop in
// unrealistic ways
Callable<Double> directCall = new DFACaller(runs);
Callable<Double> viaGetSet = new GetSetCaller(runs);
ABOUT US
double time1 = time TestRun("public fields", runs, directCall);
double time2 = time TestRun("getter/setter fields", runs, viaGetSet);
LEARN MORE
;; Java HotSpot VM
;; Just-in-time compilation
System.out.printf("%7d %,7d\t\tfield access=%.1f ns, getter/setter=%.1f ns%n",
uptime(), iterations, time1, time2);
// added to improve readability of the output
Thread.sleep(100);
}
}
}
blog
Download all listings in this issue as text
50
ORACLE.COM/JAVAMAGAZINE /////////////////////////////////////////////// MAY/JUNE 2012