[ Team LiB ] |
3.11 Native Method CallsFor 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 in 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.) I've seen native method calls used for performance reasons in earlier Java versions when doing intensive number-crunching for a scientific application and parsing large amounts of data in a 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 a parsing application would now be able to run fast enough in pure Java, but the original application was built with quite an early version. 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.[11]
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 probably cancels 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 number 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 a native call to a particular set of services producing just a couple dozen objects, but the Java-ported code producing nearly 10,000 objects. Apart from this difference, the speeds of the two implementations were similar.[12] 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.
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, 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, only a few recommendations apply:
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. |