|
By
Bernd Kreimeier
Gamasutra
June 18, 1999

|
|
Features

Dirty
Java: Optimizing Pure Java
Despite improvements
like Just-In-Time (JIT) compilation, and despite HotSpot blueprints, Java
today is by and large still inadequate for some applications, due to performance
problems. To speed up a Java application, there are two possible avenues.
The first option is to abandon the portability of "pure" Java in favor of
faster, "dirty" Java techniques; you use native (C/C++) code in conjunction
with the Java Native Interface (which will be introduced in the July issue
of Game Developer magazine), or compile Java bytecode to native code.
The second
option is to abandon a few of the noble ideas behind Java, and write Java
source code that is "dirty" in a different way. You can, for instance,
avoid the delays imposed by creating objects and having them garbage collected
in order to make your game run faster. This latter category of performance
enhancements is what I will explain in this article.
Pure
Java?
Java is
often praised for its theoretical ability run on any hardware. In practice,
this "write once, run anywhere" promise hardly covers more than a few
platforms. Depending on the packages and APIs used, it might turn out
to be Win32 and Solaris, or maybe only the former or the latter. For many,
if not most, game developers, portability is still not considered a worthwhile
goal anyway.
The Holy
Grail of Java portability is "100% Pure Java". At first glance, this is
usually interpreted as "stay away from native code". But upon closer look,
it turns out that there are quite a few concepts in Java which, while
not exactly set in stone, are strongly encouraged by the language design.
It turns out that you can gain quite a bit of speed working around the
way Java is meant to be used. Even in the absence of native code, though,
using these techniques forces you to write "dirty" Java code.
A Thin
Thread
Sun boasts
about Java's built-in support for multithreading, and it is definitely
a nice feature. What is usually not hyped are the problematic scheduling
behaviors. It is convenient to rely on a native thread implementation
when porting a Java Virtual Machine (JVM), but multithreaded code written
with, say, priorities or time slicing in mind may behave very different
when those features aren't supported within the operating system. Writing
multithreaded applications and threadsafe code is difficult enough to
begin with, but having to write possibly time-critical code without any
scheduler specification to rely on is a challenging task, indeed.
To make
things worse, there isn't really that much use for multithreading in most
games. There is no reason to design a multiplayer game server like a multithreaded
web server. Certain client loops (for example, sampling, timing and queuing
user input) might run at a fixed rate, but they also might require real-time
behavior that is not guaranteed – and often not possible – with a JVM.
A game might wind up having asynchronous sender/receiver networking threads,
but there surely is no point in running a thread for each NPC or server-side
bot.
The death
sentence for multithreading is that presently the performance penalty
for thread-safe code is significant. Symmetric multiprocessing is not
in widespread use, speed-ups with multiprocessing are difficult to accomplish,
and many JVM themselves run only on a single processor. The gains, if
any, usually do not compensate for the overhead of mutex handling and
synchronization. So it is safe to say that, until the Java multithreading
and the underlying hardware improve substantially, your game will perform
better without threading.
|