Native Method Calls Underlying JDK Improvements

- 74 - sudden performance decrease, but it was not discovered until time had been wasted checking software versions, system configurations, and other things.

3.6 Compile to Native Machine Code

If you know the target environments of your application, you have the option of taking your Java application and compiling it to a machine-code executable. There is a variety of these compilers already available for various target platforms, and the list continues to grow. Check the computer magazines or follow the compiler links on good Java web sites. See also the compilers listed in Chapter 15 . These compilers can often work directly from the bytecode i.e., the .class files without the source code, so any third-party classes and beans you use can normally be included. If you follow this option, a standard technique to remain multiplatform is to start the application from a batch file that checks the platform and installs or even starts the application binary appropriate for that platform, falling back to the standard Java runtime if no binary is available. Of course, the batch file also needs to be multiplatform, but then you could build it in Java. But prepare to be disappointed with the performance of a natively compiled executable compared to the latest JIT-enabled runtime VMs. The compiled executable still needs to handle garbage collection, threads, exceptions, etc., all within the confines of the executable. These runtime features of Java do not necessarily compile efficiently into an executable. The performance of the executable may well depend on how much effort the compiler vendor has made in making those Java features run efficiently in the context of a natively compiled executable. The latest adaptive VMs have been shown to run some applications faster than running the equivalent natively compiled executable. Advocates of the compile to native executable approach feel that the compiler optimizations will improve with time so that this approach will ultimately deliver the fastest applications. Luckily, this is a win-win situation for the performance of Java applications: try out both approaches if appropriate to you, and choose the one that works best. There are also several translators that convert Java programs into C . I only include a mention of these translators for completeness, as I have not tried any of them. They presumably enable you to use a standard C compiler to compile to a variety of target platforms. However, most source code- to-source code translations between programming languages are suboptimal and do not usually generate fast code.

3.7 Native Method Calls

For that extra zing in your application but probably not applet, try out calls to native code. Wave goodbye to 100 pure Java certification, and say hello to added complexity to your development environment and deployment procedure. If you are already in this situation for reasons other than performance tuning, there is little overhead to taking this route in your project. A couple of examples Ive seen where native method calls were used for performance reasons were intensive number-crunching for a scientific application and parsing large amounts of data in restricted time. In these and other cases, the runtime application environment at the time could not get to the required speed using Java. I should note that the latter parsing problem would now be able to run fast enough in pure Java, but the original application was built with quite an early version of Java. In addition, some number crunchers find that the latest Java runtimes and optimizing compilers give them sufficient performance in Java without resorting to any native calls. [9] - 75 - [9] Serious number crunchers spend a large proportion of their time performance-tuning their code, whatever the language it is written in. To gain sufficient performance in Java, they of course need to intensively tune the application. But this is also true if the application is written in C or Fortran. The amount of tuning required is now, apparently, similar for these three languages. Further information can be found at http:www.javagrande.org . The JNI interface itself has its own overhead, which means that if a pure Java implementation comes close to the native call performance, the JNI overhead will probably cancel any performance advantages from the native call. However, on occasion the underlying system can provide an optimized native call that is not available from Java and cannot be implemented to work as fast in pure Java. In this kind of situation, JNI is useful for tuning. Another case in which JNI can be useful is reducing the numbers of objects created, though this should be less common: you should normally be able to do this directly in Java. I once encountered a situation where JNI was needed to avoid excessive objects. This was with an application that originally required the use of a native DLL service. The vendor of that DLL ported the service to Java, which the application developers would have preferred using, but unfortunately the vendor neglected to tune the ported code. This resulted in the situation where a native call to a particular set of services produced just a couple dozen objects, but the Java-ported code produced nearly 10,000 objects. Apart from this difference, the speeds of the two implementations were similar. [10] However, the overhead in garbage collection caused a significant degradation in performance, which meant that the native call to the DLL was the preferred option. [10] This increase in object creation normally results in a much slower implementation. However, in this particular case, the methods required synchronizing to a degree that gave a larger overhead than the object creation. Nevertheless, the much larger number of objects created by the untuned Java implementation needed reclaiming at some point, and this led to greater overhead in the garbage collection. If you are following the native function call route, there is little to say. You write your routines in C, plug them into your application using the native keyword as specified in the Java development kit, profile the resultant application, and confirm that it provides the required speedup. You can also use C or C++ or whatever profilers to profile the native code calls if it is complicated. Other than this, the only recommendation that applies here is that if you are calling the native routines from loops, you should move the loops down into the native routines and pass the loop parameters to the routine as arguments. This usually produces faster implementations . One other recommendation, which is not performance tuning-specific, is that it is usually good practice to provide a fallback methodology for situations when the native code cannot be loaded. This requires extra maintenance two sets of code, extra fallback code but is often worth the effort. You can manage the fallback at the time when the DLL library is being loaded by catching the exception when the load fails and providing an alternative path to the fallback code, either by setting boolean switches or by instantiating objects of the appropriate fallback classes as required.

3.8 Uncompressed ZIPJAR Files