GCJ, a quest for speed.

In the ever present quest for speed (when concerning Java and Gaming), I took the step of trying to get GCJ running.

This is really just a quick glance at GCJ as I only downloaded it late last night and have spent most of the morning double checking the results.

I am sure almost all are familiar with GCJ, and its significance, but for those that dont GCJ is part of the GNU GCC which stands for the GNU Compiler Collection (formally the GNU C Compiler) which can be found at http://gcc.gnu.org .

GNU is part of the Free Software Foundation or FSF (www.fsf.org) and so unlike SUN are interested in promoting freedom of information (NOT just free software as some people seem to think).

And as such represents, IMHO, one of the most significant development to happen to Java.

So what is GCJ (http://gcc.gnu.org/java/)?

GCJ is a portable, optimising, ahead-of-time compiler for the Java Programming Language. It can compile:
[] Java source code directly to native machine code,
[
] Java source code to Java bytecode (class files),
[*] Java bytecode to native machine code.
*As taken from their web site.

Now before you get all excited I have to say that I did not try compiling to native code, that was not my (current) intention.

Neither did I successfully compile anything fully (!) but I got very close.

This all stems from the fact that I am utterly fed up with the Javac compiler, it’s slow and does not create impressive output in both file size and speed. Of course this is partly due to the wonderful design of the JVM (being a stack based processor) and that Javac no longer optimises the byte codes.

(Some speculation as to whether the optimisation was disabled for 3rd parties to sell their compilers or that the latest Hot Spot’s might just be doing pattern matches on byte codes and replacing with relevant machine code and so wont work on non-standard byte code combinations are both welcomed. Although unproven!)

It has gotten to the stage where the source files zipped is usually smaller than all the class files, so why not provide the source code and get much better optimisation from it?? Of course this is a discussion for another thread entirely.

It’s also interesting to note that old versions of Javac (like 1.1.6) can actually produce smaller class files. (As opposed to 1.3.0 which I regularly use.) Although this does lead to some compatibility problems.

Right then, to the point! (In an attempt to stop rambling!)

Rather than just throw a mickey mouse Applet at it, I though Id just got for broke and throw .:Redemption:. at GCJ (Version 3.2) some ~500K of source code to see what happened.

As already stated it fell over. But I then proceeded to see why.

First up : “malformed UTF-8 character.”

This seemed to be caused by my use of storing data in strings (thus using ASCII values > 127, quite a common practice dating back way before poking machine code into REM statements).

I tried using the “–encoding” switch to force a different type other than Unicode, such as Cp437 (stand DOS format), 8859-1 (ISO Latin-1) and even ASCII but to no avail it just would not take these strings.
I got round the problem by not using ASCII values > 127. It worked…

Next up was my audio system, I figured this would cause a problem and though it might not even compile. It was generating loads of missing class errors, but oddly they were all in the standard Javax.audio package.

So I assumed it could not find the classes, as I could see them, a typical CLASSPATH problem. After correcting the class path it still could not find them. Still this did not surprise me, as Java seems to have so many of these little quirks.

Instead I un-JARed the Runtime library that comes with 1.3 and made sure that the class path was now pointing to the correct location.

Still no joy, but I was sure it could see them, none of the other classes were causing trouble just the Javax.audio package.

So I decided to change the import lines to explicitly request the required classes rather than just .*

So:
import javax.sound.sampled.*;
became
import javax.sound.sampled.AudioSystem;
etc.

It worked, not one error compiling the audio system, much to my surprise!

Compiling the rest went relatively smoothly (just some unicode and audio problems, adding the current directory to the class path solved these). It then occurred to me why not try the Optimisation tags. –O2 worked, resulting in much smaller files and then –O3 worked making them a bit smaller again.

And so to execution, trying to get .:Redemption:. to boot was a no go, until I realised that I was not providing all the data files, typical mistake!

I.E. Hated it and would not even boot with a:
java.lang.InstantiationException:
at com/ms/applet/BrowserAppletFrame.newInstance

IBMs 1.3 & SUNs 1.3.1 JRE started but then fell over with:
java.lang.VerifyError: (class: ???, method: signature: (L_;Ljava/lang/ThreadGroup;Ljava/lang/String;)V) Expecting to find object/array on stack

Which is an unusual one, and looked like it was caused by my use of anonymous inner classes, as the threads seem to be defined in reverse order (not that should matter).

So copying over the threads generated by Javac was worth a try and sort of worked as .:Redemption:. would now boot get up to the point when it starts loading external data and crash with a:
java.lang.NoSuchMethodError

I then decided to copy the class that controls all the threads, that was compiled with Javac…

And BEHOLD! It worked!

.:Redemption:. was running under any browser or Applet viewer (although I have yet to try all).

But a quick dabble proved that all was not well, my audio system was not working correctly, the default setting was fine, try changing the interpolation and it would go all funny.

On investigating this code it turns out GCJ was creating the wrong set of byte codes (it does not matter whether optimisation is on or not) for a specific situation:

For example:

public class Test1
{

  private int[] iBuffer = new int[ 100 ];
  
  public Test1()
  {
    int iScale1 = 128;
    int iScale2 = 64;
    
    for( int c = 0; c < iBuffer.length; )
    {
      double dValue = Math.random();
      iBuffer[ c++ ] += dValue * iScale1;
      iBuffer[ c++ ] += dValue * iScale2;
    }
  }
};

Would produce the main loop as follows, (which is wrong):

for(int c = 0; c < iBuffer.length;)
        {
            double dValue = Math.random();
            iBuffer[c++] = (int)((double)iBuffer[c++] + dValue * (double) iScale1);
            iBuffer[c++] = (int)((double)iBuffer[c++] + dValue * (double) iScale2);
        }

(Note this error only seems to appear when using doubles, using ints did not cause it to occur.)

This can be fixed by changing the code to as follows:

for(int c = 0; c < iBuffer.length;)
        {
            double dValue = Math.random();
            iBuffer[c++] = (int)((double)iBuffer[c] + dValue * (double) iScale1);
            iBuffer[c++] = (int)((double)iBuffer[c] + dValue * (double) iScale2);
        }

No big deal, most obfuscation and optimisation program usually have similar quirks.

Which fixed it, although there is something else wrong with my audio system but I have not bothered tracking it down yet.

So at the moment I appear to have a ~stable version of .:Redemption:. mainly compiled with GCJ.

GCJ compiled the lot considerably quicker than Javac.

It would be nice if the gaming community started trying to fix/document some of these problems we are having/going to have so that GCJ can be fixed and fully working. As we stand to gain the most from it.

I would be very much interested in hearing anybodys elses experiences with GCJ.

Also if there is enough interest in this topic I will quite happy write a quick tutorial on how to get GCJ up and running on a Windows 98 system (I assume Unix guys will already know) for anybody willing to have a go. It is not that difficult either, which is a bonus.


Woz.

P.S.
As always sorry for the big post!

A tutorial would be most welcome for many I’m sure.
(Although I’m using Jet - point and click :slight_smile: Hehe)

Cas :slight_smile:

Thanks for your report! I have so far not tested GCJ, but was interested in it, so your report is interesting for me.
It is very disapointing that GCJ produces wrong code, that makes it unusable for me. I rather will accept 10% bigger class files than 30% more time spent on debugging (I program enough bugs myself already…)

Have you tried compiling to native code? I did play about with GCJ myself a couple of months ago, and got 2Mb exe for hello world! I didnt try anything larger as nio support wasnt in at the time, so no good for using lwjgl. I had a quick look at the gcj homepage a week or so ago and I think that nio is now in.

I would be interested in your results as most of this 2Mb should be overhead, and not grow too much as the program size increases.

Might start playing with this again after my exams.

I bet there’s very little can be done about that 2MB overhead - I did an experiment recently to see how many classes I could delete out of rt.jar but in the end I still had a compressed JVM distribution that weighed in at over 2MB. There’s a ton of stuff loaded (unnecessarily) behind the scenes.

Cas :slight_smile:

Its missing support for the awt so unless you are using lwjgl or you are using it for server side applications you won’t have much use for it.

Anyone know when it might have support for the awt and be 1.4 compliant?

Just downloaded the latest minGW release of gcj, and it seems to have some AWT support in there. The classes are in the library anyway. Not sure how functional it all is though. I’ll throw a few programs at it and let you all know. The bad news (for using it with lwjgl) is that nio support still isnt in there.

GCJ is far from complete, but it would be interesting to find out exactly what areas we require and find out if the existing does/does not work.

Themroc, a quick comparision shows that the class files generated by GCJ are larger than Javac. This does not suprise me, but we might yet be able to get the file sizes down. I was expecting more errors than what I’ve got so far, but you would be very surprised to find the number of errors I’ve seen in different JVM’s and Java.

The errors (obscure though some might be) need reporting so they can be removed. But need to be found first!!

Jacko, no I haven’t tried compiling to machine code, although a 2 meg exe might sound a lot the chances are this is the base foot print, adding 100K of class might only result in a 2.1 meg exe.

Which might still sound big but is still considerably smaller than the Java Runtime foot print!

We shall have to investigate.

(Quite a few other languages suffer from this too.)

Cas the tutorial is on the way, should be on the net tomorrow morning after I re-read it for stupid mistakes… ::slight_smile:


Woz.

Found this tonight…

A benchmark program translated into various languages, and more interestingly run on different JVM’s and also under GCJ.

http://www.coyotegulch.com/reviews/almabench.html

I havent looked at his benchmark program so I dont know if he’s doing anything nasty in java, but things do look good for GCJ.

And from what I can tell from a quick reading of the article very bad for Suns 1.4 implementation.

[quote]Found this tonight…

A benchmark program translated into various languages, and more interestingly run on different JVM’s and also under GCJ.

http://www.coyotegulch.com/reviews/almabench.html

I havent looked at his benchmark program so I dont know if he’s doing anything nasty in java, but things do look good for GCJ.

And from what I can tell from a quick reading of the article very bad for Suns 1.4 implementation.
[/quote]
thanx for that - a very informative and interesting read.

As promised,

There is now a very simple tutorial on how to get GCJ V3.2 running on Windows 98.

Anybody should be able to get it running. :smiley:

Which can be found here:
http://realmdesigns.russki.net/?command=tutor&t=0
(Although I’m not to happy with the layout, and perhaps I should have done it on multiple pages to save bandwidth but hay!)


Woz.

Thats a really good article you put together Woz. Thanks for using MinGW over Cygwin :). Is there still issues with using MinGW with GCJ or have these been resolved?

Mr Bob,

Hope things aren’t to damp for you, it’s been pretty wet round here…

MinWG made GCJ so much simpler to setup than Cygwin, although once GCJ is up and running their is little difference - other than Cygwin being FAR more powerful!

I didn’t come across any issues/problems with MinWG & GCJ, although I must stress this is early days yet, they appeared to be functioning fine.

It would be interesting to know what these issues are (I assume this was for an earlier version??) but if you have a link, or know exactly what thet are, it would be much Appreciated!


Woz.

Woz-

I have not tested GCJ myself yet but have noticed on the site news this message:

Its a bit dated of a post so I am figuring this issue has been resolved already. I was just curious if you knew of any other issues still outstanding.

I guess the best way to find out is to just port a few apps over and see what happens.

Bob,

Ahh, I thought you ment there was an issue between MinGW and GCJ! ::slight_smile:

Yes, there are LOTS of problems with GCJ when refering to Java as the GNU libraries (libgcj) are far from complete.

How ever libgcj should only be neccessary if you are compiling to machine code or using gij (GNU Interpreter for Java) to run Java classes via gcj.

Even then it is apparently possible (IOW I haven’t tried it my self) to run software such as http://www.eclipse.org/ in gij.

So this was what I was hoping to find out, exactly how much of the libraries are complete or needed by us to get “things” working.

Here are the two main articles that sparked me into having another go with GCJ (tried ages ago and got no where).

Running Eclipse with GIJ:http://www.klomp.org/mark/gij_eclipse/index.html

and

Compiling Java with GCJ by Per Brothner:http://www.linuxjournal.com/article.php?sid=4860


Woz.

P.S.
I have now added instructions on how to get GCJ running under the NT series (NT3.51, NT4, 2000, XP etc).
http://realmdesigns.russki.net/?command=tutor&t=0

Just to keep people informed about GCJ V3.2,

I have got the odd Java Application to compile to a .exe, although these are Applications that are devoid of a UI and that don’t rely on the AWT or SWING as these don’t exist in GCJ V3.2 (the odd stub files found lying around in the MinGW directory are not connected to anything higher up).

After further looking around it looks like the only GUI that will work with GCJ V3.2 is SWT (http://www.eclipse.org).

I thought about various others that might work, such as JavaGL but came to the conclusion that these all rely on the AWT - which doesn’t exist yet.

So what is required is a DLL interface to the outside world via JNI.

An article by Kirk Vogen about get SWT to work with GCJ can be found here: http://www-106.ibm.com/developerworks/library/j-nativegui/. Although concerning GCJ 3.1 and Linux.

A Post in the GCC mailing list by Ranjit Mathew discusses how to get SWT and GCJ V3.2 to work with Win32 : http://gcc.gnu.org/ml/java/2002-12/msg00105.html

GCJ V3.3 will hopefully change this, although from what I’ve read it looks like lots of features will end up in V3.4 but I could not find a release date for this.

GCJ V3.3 is due for March 1st 2003 :D, although it will take time before a version in MinGW is available.


Woz.

Couldn’t care less about AWT :slight_smile: So long as it can JNI to LWJGL that’s just great.

Cas :slight_smile:

Squeeto has kindly pointed out that Mohan Embar has managed to cobble together a preview of GCJ V3.3.

Now its even simpler to get GCJ V3.3 running as MinGW has already been included and its just a case of unzipping the file and adding the “bin” directory to your path.

However it is a big download (and remember it is JUST a preview - some of the patches may not be allowed into the final build) at some 25Meg.

It’s also appears to be on a slow connection, took me 3 hours :stuck_out_tongue: on my modem.

The main goal seems to be to get SWT working better, as well as some bits like processes.

I managed to get bother processes (missing in V3.2) and SWT running with this preview version.

the Preview GCJ V3.3 can be found at:
http://www.thisiscool.com/gcc33_mingw.htm

But remember that the FULL version of GCC V3.3 is due in a couple of weeks anyway (fingers crossed), but I thought I’d let you all know.


Woz.

P.S.
Take it easy.

I might get Brian to compile Alien Flux with it (seeing as he’s got a willy-waving cable modem and can spare a 25Mb download…). I’ve just bought Jet at a grand cost of £500 and I’ll be incredibly miffed of course if gcj is faster :o

Cas :slight_smile:

[quote]I might get Brian to compile Alien Flux with it
[/quote]
lot’s of these:

warning: unreachable bytecode from 277 to before 280

where 277 & 280 obviously vary.

and this one too:

xap/features/EntityFeature.java: In class `xap.features.EntityFeature':
xap/features/EntityFeature.java: In method `xap.features.EntityFeature.tickAllEntities()':
xap/features/EntityFeature.java:385: error: class 'xap.features.EntityFeature$EntityInstance' has no method named 'inCollisionWith' matching signature '(Lxap/Entity;)V'
xap/features/EntityFeature.java:386: error: class 'xap.features.EntityFeature$EntityInstance' has no method named 'inCollisionWith' matching signature '(Lxap/Entity;)V'
xap/features/EntityFeature.java:386: confused by earlier errors, bailing out