Optimizing Java

Published October 30, 2000 by Ted Bradley, posted by Myopic Rhino
Do you see issues with this article? Let us know.
Advertisement
I recently got my hands on Vampire the Masquerade: Redemption (VTM:R), the game is brilliant and got me hooked for a whole weekend completing it. At the end of it the beast within me rose and I wanted more blood, so I downloaded the SDK. Much to my horror and perverse delight, I discovered that the scripting engine at the heart of VTM:R, is embedded Java. All of the game mechanics can be controlled and overridden via Java. A lot of games are probably going to be following suit for a lot of reasons, Java is a freely available language, supported on just about every platform. It has the Java Native Interface (JNI), the two way link between Java and the native code, so all you have to do is generate a load of Java classes which link into you game's functions and allow the game access to Java objects. Anyway this is very exciting for me as I've been working with Java for a year now, and my ambition is to get into the games industry.

The problem is though, that despite all the advances with Just In Time compilers (JITs) and other speed improvements, Java is still slower than native languages. All is not lost though, there are a few things, which if you remember to do while coding, can increase the execution of your Java code by up to 250% in places! Games need to be fast, so here's some ways of squeezing that last caffeinated drop of performance from Java...

Rules of Optimization:
1. Don't do it.
2.(For experts only) Don't do it yet"
- M.A. Jackson


The three main things which affect performance in Java are:


[size="5"]API Selection

The Application Programming Interface (API) which comes with Java is a standard library of classes across all platforms, the only differences are between the different versions of Java, 1.0,1.16,1.18,1.2,1.22,1.3 etc, and the different editions, Java Standard Edition, Java Micro-Edition Java 2 Enterprise Edition etc. You don't need to worry about that for now though. Basically the rule here is that with a lot of the classes in the api they make use of JNI calls to faster native implementations. For example, rather than use the following piece of code to copy an array:

for(int i = 0; i < source.length; i++)
destination=source;
Use the following API call instead:

System.arraycopy(source,0,destination,0,source.length);
This makes use of some native code and is much, much faster. Wherever appropriate make use of API calls. Bear in mind, however, that use of classes such as the Collection classes which take a generic Object, can prove to be a lot slower than if you used a class of your own specific to the type of Object you are using, as you are otherwise hit with a lot of casts. An extreme example would be if you where to try and store ints in a Vector. As an int is a primitive, you also have to convert it to an Integer to add it to the Vector:

aVector.addElement(new Integer(anInt));
When it comes to retrieving that int you have an even worse situation, you have to cast the returned Object to an Integer and then call the intValue() method on it:

anInt = (Integer)aVector.elementAt(index).intValue();
A bit of an extreme example I'll agree, but it shows a situation in which the use of a custom class could speed things up considerably.


[size="5"]The Compiler

Most people who have used languages such as C/C++ are used to reams of options to optimize their code by doing things such as targeting different processors, different memory models etc. etc. In Java we are lucky to have even the one option we do, and most people don't even know it exists! The option I'm talking about is the -O option which turns inlining on.

Inlining is when chunks of code are copied across from one part of the source to another so as to speed up execution. With C/C++ all you have to do is prefix the function with the inline keyword and the compiler does it for you. In Java you tell it to consider inlining functions and variables when you compile with the -O option. For a method/variable to be considered for inlining it must meet the following criteria:
  • It must be declared as either private, final or static.
  • It can't be synchronized.
  • Inlined methods can't contain local variables. Once javac (the compiler) has this list of candidates, it goes through them and makes the following decisions:
    • If it's a variable, it will usually treat it like a #define in C/C++ and place the value directly in the code, rather than a reference.
    • If it's a method it will decide whether it thinks it is a good idea to inline or not. In most cases it only inlines short methods, about 2 or 3 lines long. There's a couple of tricks you can use with this:

      [size="3"]Trick #1

      If there are a few methods in your program which are proving to be bottlenecks and they can't be inlined, a possible solution is to make a subclass of the class they are contained in and then making them final or private. Bear in mind that this is very bad practice and by no means an ideal solution.

      [size="3"]Trick #2

      Static final constants get stored in a class file as well as being inlined. Try putting them in an interface and then implementing that interface in each class which uses them, that way they only get stored once and the calls to them are still fast.

      [size="3"]Static and Dynamic binding

      Another thing which the -O option affects is static/dynamic binding. When some code calls a method it either dynamically or statically 'looks it up'. With static binding the code knows exactly where the method is going to be and directly calls it. With dynamic binding the code does not know where the method is and has to look for it at runtime. All of this is transparent to the coder. It's the Java Virtual Machine (JVM) and compiler which has to worry about this. Because of Java's architecture, most things are dynamically bound, that means that only the class you modify has to be recompiled. When you use the -O option, it allows static binding for methods and fields which are only used within a class, and cannot be used outside it. Static binding is much faster than dynamic binding.


      [size="5"]The JIT

      The JIT reads ahead through each class and compiles it into native code before it is executed. This results in improved execution time, but slower object creation. With most applications, the JIT speeds things up, but with some, where lots of Objects are being created for example, it can actually slow things down. Some JITs have a lot of options which can alter the tradeoff, but many don't. If you want to turn off the JIT, it can be done with the following option:

      java -J-Djava.compiler=none MyClass

      [size="5"]Some Miscellaneous Speed Demons

      [size="3"]Strings

      Strings are horrendously slow, take the following example:

      for(int i = 0; i < source.length; i++)
      String x;
      String y = "Oh";
      String z = " No!";
      x = y + z;
      At first glance it doesn't look too bad on surface, but in reality it is one of the worst things for slowing things down when you do a lot of String operations, here's what happens. System.arraycopy() is used to copy y and z into a temporary StringBuffer and then toString() is called on the temporary StringBuffer. Try using StringBuffer directly when performing operations such as these.

      [size="3"]I/O

      Always wrap a BufferedReader around an InputStream, and a BufferedWriter around an OutputStream. Reading one byte at a time from a stream is just too slow.

      [size="3"]Graphics

      Override the repaint() method and use clipping when implementing custom graphics. Use double buffering.

      [size="3"] Static Initializers

      Make use of them, for those that don't know, they are a block of code contained in curly braces which gets called the first time an object is initialized. They are very handy for all sorts of things, and can be use to do constructor work which only needs to be done once for all instances of a class.

      [size="3"]Profiling

      Profile your classes to determine the bottlenecks. A profiler which comes with java is the -prof option. Run your class using java -prof myclass. Java will dump information about the execution to the local directory.

      [size="3"]Make use of lessons learned with other languages

      A lot of optimizing techniques apply to all languages, use them. For example read through the article: Performance Programming in C++, by Jorris Timmermans, it has a lot of content which can be applied to Java among others. Most important are the use of bit shifts and loop optimization.

      [size="3"]Being careful about which primitives you use

      As a rule of thumb, ints are faster than longs and floats are faster than doubles, use them in preference wherever possible.

      [size="3"]Make use of threads

      This isn't an optimization thing, but make use of threads when responding to GUI actions, rather than locking the application, the GUI will appear to be much more responsive, than if all the processing was done before the handler returned.


      [size="5"]Final Note

      These are basically all a collection of ideas I have come across in my year as a Java Programmer so far, I'll probably come across many more in the future. None of the things above are laws, they aren't necessarily the fastest ways.

      As well as in books which I don't remember the title of ;-) further information can be found on byte codes and the like at: http://www.cs.smu.edu/~jch/java
Cancel Save
0 Likes 0 Comments

Comments

Nobody has left a comment. You can be the first!
You must log in to join the conversation.
Don't have a GameDev.net account? Sign up!
Advertisement