Journal Journal: Java and C++ optimisation, and GCJ
I've been trying to compile gcj on my Mac (having had to use my home linux box at work recently, I'm using the Mac as my home 'PC'. Actually I kind of like it
- An IBM article on how gcj didn't really compare to the IBM VM...
- A slashdot discussion on the above IBM article
Now what surprised me was just how badly gcj was doing on the benchmarks he'd written - even *if* (and I make no accusations, just note that IBM's VM won...) it was a PR piece dressed up as an article. I decided to check out the performance on a linux box I could ssh to...
Here's the java code: (slightly edited to look better in slashcode)
import java.io.*;
class prime
{
private static boolean isPrime(int i)
{
for(long test = 2; test < i; test++)
if (i % test == 0)
return false;
return true;
}
public static void main(String[] args) throws IOException
{
long start = System.currentTimeMillis();
long n_loops = 50000;
long n_primes = 0;
for(int i = 0; i < n_loops; i++)
if(isPrime(i))
n_primes++;
long end = System.currentTimeMillis();
System.out.println(n_primes + " primes found");
System.out.println("Time taken = " + (end - start));
}
}
First off, this is a truly awful algorithm for finding primes, but it's the code he provided... In any event it certainly tests loops a lot [grin]. The author didn't provide a comparable C/C++ program so here's one I prepared earlier:
#include <stdio.h>
#include <sys/time.h>
# define timersub(a, b, result) \
do { \
(result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \
(result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \
if ((result)->tv_usec < 0) { \
--(result)->tv_sec; \
(result)->tv_usec += 1000000; \
} \
} while (0)
static int isPrime(long i)
{
for (long test=2; test<i; test++)
if (i%test == 0)
return false;
return true;
}
int main(int argc, char **argv)
{
struct timeval stt,end, dt;
gettimeofday(&stt, NULL);
long n_loops = 50000;
long n_primes = 0;
for (long i=0; i<n_loops; i++)
if (isPrime(i))
n_primes ++;
gettimeofday(&end, NULL);
timersub(&end, &stt, &dt);
printf("Time taken: %d.%06d secs\n", dt.tv_sec, dt.tv_usec);
printf("Primes : %d\n",n_primes);
}
... which is pretty much as direct a copy of the java version as I can make. The programs were both compiled using -O3 and run, vis:
[simon@cyclops
/tmp]% gcj --main=prime -O3 prime.java -o prime_j
[simon@cyclops/tmp]% ./prime_j
5135 primes found
Time taken = 15095
[simon@cyclops/tmp]% g++ -O3 prime.cc -o prime_cc
[simon@cyclops/tmp]% ./prime_cc
Time taken: 7.060192 secs
Primes : 5135
Which would appear to indicate that the java code is approximately 50% of the speed of the C++ code. BUT (you knew there was a 'but', right ?) gcj is notoriously bad at optimising long integers. I suspect it actually does the top 32 bits, then the bottom 32 bits, then combines the results... If we change all occurrences of 'long' to 'int' in the arithmetic (not the time variables), we get very different results:
[simon@cyclops
/tmp]% cp prime_int.java prime.java
[simon@cyclops/tmp]% gcj --main=prime -O3 prime.java -o prime_j
[simon@cyclops/tmp]% ./prime_j
5135 primes found
Time taken = 7061
[simon@cyclops/tmp]% cp prime_int.cc prime.cc
[simon@cyclops/tmp]% g++ -O3 prime.cc -o prime_cc
[simon@cyclops/tmp]% ./prime_cc
Time taken: 7.061838 secs
Primes : 5135
So, when you use 'int' variables, gcj is pretty much as good as g++ for this benchmark. What does this prove ? Not very much, apart from you should always take published figures with a pinch of salt when someone has a vested interest, and that the IBM's VM 64-bit maths is better than GCJ's...
It just irked me that an entire article could be based on something so simple. I've always been reasonably impressed with the speed of GCJ, but perhaps that's because I tend to use 'int's in my loop variables rather than 'long's. I can't quite rid myself of the suspicion that the IBM author was making cheap capital out of a small thing, as well...
I'm a great fan of Java (and compiled java). I find it a lot easier to write programs in, and far and away easier to maintain. I've got a fighting chance of opening up a colleagues JBuilder project and understanding what they've done (even though my colleagues tend to regard comments as optional, [sigh]). In C++ I have to worry a lot more about memory allocation - mainly in terms of the policy for release of objects and their private/protected data. This can truly be a nightmare
IMHO one of the real 'wins' of GCJ is how easy it is to extend it with native bindings. I ported the JSDL SDL bindings for Java to gcjsdl in a matter of days because CNI is *much* nicer to work with than JNI.
I think it's fair to say that my compiled language of choice is now Java, with C++ as needed to bind external libraries. I think that says it all...
Simon