Weighing in ON Java Native CompiLation
CONTENTS:
Code Compilation Basicsabout The Test Setuptest 1: Prime.Javatest 2: Scimark 2Pros and ConsconclusionResourcesoSabout The Authorrate this Article
Related content:
Bridging the gap to comdebugging integrated Java and C / C Code
Subscriptions:
DW Newslettersdw Subscription (CDS and Downloads)
The PROS AND CONS OF Generating Native Code from Java Source
Martyn Honeyford
(Martynh@uk.ibm.com) Software Engineer, IBM UK Labs01 Jan 2002
When it was first introduced, it seemed that Java native compilation would surely topple the JVM, taking with it the Java platform's hard-fought platform independence. But even with its growing popularity and the increasing number of native compilers on the market, native compilation has a way to go before it poses a real threat to Java code's portability. Unfortunately, it also may be a while before the technology is mature enough to resolve the Java performance issues so many of us struggle with today. Share your thoughts on this article with The author and other readers in the
Discussion forum by Clicking
Discuss at the top or bottom of the article.
Despite its many high points, there are still several issues with the Java language that rule out its use in key projects. These include execution speed, memory footprint, disk footprint, and JVM availability. JIT compilers do much to improve the platform's execution speed and J2ME greatly reduces its memory footprint, but in many domains Java applications simply can not compete with their native (typically C / C ) counterparts. to resolve these problems, many developers have turned to Java native compilers, which allow applications to be written in the Java language and then compiled into native executables. This solution will cost you in terms of platform independence, but it can result in the faster execution and smaller footprint essential to so many of today's applications. to bring you up to speed on Java native compilation technology, We'll Start With a Discussion of The Basics of Code Compilation, Including a Brief Overview of Why Many Developers Are Employing Java Native Compile rs for their applications. Next, we'll test the results of Java native compilation, using a free software compiler and two different applications (one very simple, the other more sophisticated). These examples and the resulting metrics will serve as a first- . hand look at how the recent Java native compilers compare with the JVM code compilation basicsTo follow the discussion in this article, you should be familiar with the three most common methods of code compilation:
Compiling Java code with a Java compiler such as javac Compiling native code such as C / C targeted to a specific hardware / operating system (OS) platform Compiling Java code using a Java native compiler targeted to a specific hardware / OS platform Compiling Java code using a Java compiler is straightforward. We simply write the source code in the Java language, use a Java compiler to compile the source into Java bytecode, and execute the results on any hardware / OS platform that has a JVM installed. Java's reliance on the JVM for its signature "write once, run anywhere" portability is its downside; not only must a JVM be available for any platform on which you want to run your Java apps, but there must be significant system resources (memory and disk space) to support That JVM. AS A Result, Many Developers Continue To Rely On Less Flexible But More Targeted Languages Such AS C / C . Compiableing Source IN C / C
is similar to doing so in Java. Once the code is written, we run it through a compiler and linker targeted to a specific hardware / OS platform. The resulting application will be executable only on the targeted platform, but will not require that a JVM be installed (though it may require some supporting shared libraries, depending on language used). All but the most simple applications developed using this method must be tailored individually to each hardware / OS platform on which you want them to run. The third method attempts to bridge the best of each of the above solutions, allowing developers to write applications in the Java language and compile them into native executables. Once the Java code is written, it can be run through a Java compiler to produce Java bytecode, which is then compiled into native code, or it can be run directly into a Java native compiler. The number of steps involved depends on the requirements of the compiler used. The advantage of this approach is that the resulting code can be executed on the targeted platform without the JVM. This is intended to result in Java applications that execute at much improved speeds and require significantly less disk space and memory to run (though it may be necessary to provide supporting libraries for the Java native compiler). Compilers vary in the platforms they target, the level of Java support they provide, and the amount of system resources they use. You'll find a listing of some of the currently available native compilers in this article's Resources section. About The test setupit is Well Beyond The Scope of this Article To Compare The Features and Performance of Every Native Compiler on The Market. INSTEAD, I '
ve used one compiler, the GNU Compiler for the Java Programming Language (GCJ), as an example to detail the process and results of native compilation. GCJ is one of the compilers developed for the GNU Compiler Collection (GCC), which is part of the GNU project. As is true of all the software that comes out of the GNU project, GCJ is free software in both senses of the term, and therefore can easily be obtained (see Resources). If you're seriously considering the native compilation route for your product, you should obviously evaluate as many compilers as you can, perhaps using the criteria established in this article. My test system hardware consists of a PC with a Pentium II processor running at 450 MHz and containing 320 MB of memory. The OS is a recent install of the Mandrake 8.1 Linux distribution. This distribution comes with version 3.0.1 of GCJ, which is included in GCC 3.0.1 and ships as part of the 8.1 Mandrake distribution. I've run two separate applications, one Very Simple and one that is more complex. To compare the system's performance against that of the Java platform, I compiled the applications into Java bytecode. I compiled the Java code using the Sun JDK version 1.3.1.02 for Linux, then tested the resulting class on the following JVMs: Kaffe 1.0.6 Sun JVM 1.3.1_02 IBM JRE 1.3.1 For the purpose of this article, I've measured execution speed, execution memory overhead, and disk space Test 1:. Prime.javaThe first test application is very Simple, Consisting of a Single Class, Prime.java. This Application Implements a Very Basic Algorithm To Search for Prime Numbers. Listing 1 Shows The Source Code for Prime.java.
Import java.io. *;
Class Prime
{
Private Static Boolean Isprime (long i)
{
For (Long Test = 2; test
{
IF (i% test == 0)
{
Return False;
}
}
Return True;
}
Public static void main (string [] args) throws oException
{
Long Start_time = system.currenttimemillis ();
Long n_loops = 50000;
Long n_primes = 0;
For (long i = 0; i { IF (Isprime (i)) { n_primes ; } } Long end_time = system.currenttimemillis (); System.out.println (n_primes "primes found); System.out.println ("time taken =" (end_time - start_time); } } As you can see, the code loops from 0 to 50000. As it goes, it attempts to divide each number it encounters by every number up to itself, to find out if there is a remainder. (This is, admittedly, the brute -force method of ferreting outprimes, But it will succe.java INTO a Native Executable with the commit: gcj prime.java -O3 --main = prime -o prime The argument -O3 means "optimize for speed"; argument --main tells GCJ which class contains the main method to be used when the application is run; and argument -o Prime Names The Resulting Executable. For a ful set of commnd-line arguments, see the gcj documentation. To Compile The Java Bytecode Test, i buy the command: /usr/java/jdk1.3.1_02/bin/javac -o prime.java next, I invoked the code for each of our test jvms, using the folcing commands: Native: ./prime kaffe: / usr / bin / java prime sun jdk: /usr/java/jdk1.3.1_02/bin/java prime IBM JRE: / OPT / IBMJAVA2-13 / JRE / BIN / JAVA PRIME TEST RESULTS for prime.javaAs previously mentioned, I tested for execution speed, memory use, and disk space use The following tables detail the results of the first test Table 1. prime.java: Execution speedImplementationTime in milliseconds (average of three runs - lower.. Score is better) Native40180kaffe75456sun JDK67315IBM JRE18188 TABLE 2. Prime.java: Memory Usage ImplementationVM size (KB) VM RSS (KB) Native70243528Kaffe88883564Sun JDK1695606636IBM JRE819366288 Note that the VM size equals the total size of the image of the process. This includes all code, data, and shared libraries used by the process, including pages that have been swapped out. VM resident set size (RSS) is equal to the size of the part of the process (code and data) that actually resides in RAM, including shared libraries. This gives a fair approximation of how much RAM a process is using. in simple terms, if a process allocates a large amount of memory it will show up in the VM size, but it will not show up in VM RSS until it is actually being used (for example, read or written). VM RSS is actually The More Important Measure, Because It Gives A Greater Indication of The Performance Hit on The System. Table 3. Prime.java: Disk Space Usage ImplementationTotal compiled size (bytes) Native 22268Java classes962 Note that the measurements shown in Table 3 exclude shared libraries and the JVM, and are measured with the executable stripped Test 2:. SciMark 2For the second test I employed a more complicated Java application, the SciMark 2 Java benchmark. The command-line version used for this article is available for free (see Resources). SciMark 2 is quite a sophisticated application. It implements a number of benchmarks that are intended to accurately measure the efficiency of a JVM. I used The Following Command to Compile Scimark 2 INTO A Native Executable: GCJ-3.0.1 -O3 CommandLine.java random.java fft.java sor.java stopwatch.java Sparsecomprow.java lu.java kernel.java montecarlo.java --main = jnt.scimark2.commandline -o scimark And i used this command to compile the application inTo java bytecode: /usr/java/jdk1.3.1_02/bin/javac -o * .java The SciMark 2 benchmark can be run in two modes, normal and large. The mode you use determines the size of the problem sets used. I've run the tests in both modes. To invoke the code in normal mode, I used the following Commands: Native: ./scimark kaffe: / usr / bin / java jnt.scimark2.commandline sun jdk: /usr/java/jdk1.3.1_02/bin/java jnt.scimark2.commandline IBM JRE: / OPT / IBMJAVA2-13 / JRE / bin / java jnt.scimark2.commandline for the larger problem: Native: ./scimark -large kaffe: / usr / bin / java jnt.scimark2.commandline -large sun jdk: /usr/java/jdk1.3.1_02/bin/java jnt.scimark2.commandline -Large IBM JRE: / OPT / IBMJava2-13 / jre / bin / java jnt.scimark2.commandline -large Test results for SciMark 2The following tables show the results of compiling SciMark 2. Note the difference in results for the normal and large modes. Table 4. SciMark 2, Mode Normal: Execution SpeedimplementationComposite Score (Average of Three Runs - Higher Score is Better) Native15.22Kaffe7.01Sun JDK22.86IBM JRE25.29 Table 5. Scimark 2, Mode Normal: Memory USAGE ImplementationVM Size (KB) VM RSS (KB) native97885956kaffe88884092sun JDK1696927428IBM JRE819647408 Table 6. Scimark 2, Mode Large: Execution Speed ImplementationComposite Score (Average of Three Runs - Higher Score is Better) Native8.78kaffe5.72sun JDK12.04IBM JRE15.04 Table 7. Scimark 2, Mode Large: Memory Usage ImplementationVM Size (KB) VM RSS (KB) native628859072kaffe5805659072k jdk16969264624ibm jre8196457704 Table 8. Scimark 2: Disk space usage for Both Modes ImplementationCompiled size (bytes) Native49588Java Classes16318 Once again, the measurements in Table 8 exclude shared libraries and the JVM, and are measured with the executable stripped. Pros and cons of native compilationAs should be apparent from the above test results, the success or failure of . Java native compilation is far from clear cut some of the benchmarks show the natively compiled executables to be faster than some of the JVM versions; others are slower Similarly, the speed of some operations varies wildly between different JVMs The "working set".. memory tests performed show that there is not a vast amount of difference in the memory usage during execution. Tests employing different garbage collection schemes on both the native and JVM tests could further explore this area. The native version is a clear winner over the JVM Version ONLY WHEN IT COMES To Disk Space, and this is true osp place. While the classes Themselves Are Very small, the JVMs tested were huge (a recursive directory listing in the jre subdirectory of both the IBM and Sun JVMs shows that the JREs alone take up over 50 MB of disk space). But bear in mind that there are much smaller JVMs available and , while the combination of JVM and a single application was much larger than that of a native executable and the GCJ runtime library, libgcj.so (which is under 3 MB), the executable size for the native version was much larger. Thus, in situations where a large number of applications are required, the JVM version may ultimately be the winner In addition to these somewhat nebulous results, a number of potential problems can arise from the use of Java native compilers They are as follows..: Loss of platform independence: In reality, this is not so much of a problem Because the source is written in the Java language, you still have the option to produce a Java bytecode version that will run anywhere, then use native compilers on certain. platforms as required Class support / compiler maturity:. Some of the compilers are still relatively immature and may not support all the Java classes required by your application For example, while GCJ supports most Java language constructs up to v1.1 of the specification,. it does not support all the Java class libraries that typically ship with a JVM Most notably, there is very little support for AWT, making GCJ unsuitable for GUI applications Different compilers support differing levels of class library;.. Excelsior JET is one compiler that Claims to Completely Support AWT and Swing. Support / Complexity: As this field is a relative, it is often not very well usten, diagnostic tools can be becom. Diagnostic Tools Can Be Somewhat Thin On The GR ound, which makes it potentially more difficult to diagnose problems that occur in natively compiled Java apps (particularly if the error does not occur in the Java bytecode version!). ConclusionAs is generally the case when it comes to application development, the only way To really determine if java native compiration is the answer to your particular set of circumstances is to run through: Determine exactly what problem (or problems) you are hoping to solve with native compilation. Take a look at the available native compilers and come up with a handful that look like they could solve your problem. Try all the compilers you've selected with your application and see what happens. Despite the relative immaturity of the technology and the lack of clear-cut results, Java native compilation is an exciting new area for the Java language. The best way to take advantage of the existing options is to research and test them yourself, perhaps using some of the methods and criteria established in this article. While native compilation is not the JVM killer that many people thought it would be, it has proven to be just the right solution for some applications and environments. Native compilation Extends The Use of Java Language Into Domains Where it Simply Wasn't Applicable Just A Few Short Years ago. this can notle be a good thing for the java language and for the java community as A Whole. Resources Participate in the discussion forum on this article. (You can also click Discuss at the top or bottom of the article to access the forum.) To learn more about GCJ and the GNU Compiler Collection, visit the GNU Compiler for the Java Programming Language homepage . SciMark 2.0 is a composite Java benchmark for measuring the performance of numerical code in scientific and engineering applications. See the SciMark 2.0 home page to learn more about this sophisticated application. Learn how to reuse code that was not written in the Java language in "Bridging the gap to COM" (developerWorks, October 2001). When you can not employ a pure Java language solution in an application, you can still effectively debug the Java / C hybrid. Matthew White explains how in "Debugging integrated Java and C / C code "(developerWorks, November 2001). Find more Java resources on the developerWorks Java technology zone.About the authorMartyn Honeyford graduated from Nottingham University with a BS in Computer Sc ience in 1996. He has worked as a software engineer at IBM UK Labs in Hursley, England, ever since. His current role is as a developer in the WebSphere MQ Everyplace development team. When not working, Martyn can usually be found either playing the .