Every now and then people get into silly arguments about which programming language is “faster.” I always ask, faster for what? Faster compile-run-test cycle, faster to write correct programs, faster to run…? Besides, each language is tuned for certain usage patterns, making like to like comparisons impossible.
When Java became popular in the late 90s it was said it would not be fast enough for number crunching applications. Java was executed by an interpreter, too separated from the hardware to get top performance. For example, the Mandelbrot set program from my previous post should clearly be faster when written in C.
Let’s test if this is the case. Here is the program from before, ported to C with minimal changes. This is not a “micro benchmark:” the idea is to test two complete programs, that make a computation and write the results to a file. To save the generated image in a file I use the GDK Pixbuf library that supports different file formats, similar to
ImageIO in the standard Java library. Notice the difference in the two image libraries: In C the pixel value is written directly to memory through a pointer, but in Java I use the
BufferedImage.setRGB method that has to go through several abstraction layers and method calls to actually write the value to a memory.
When I compile the two versions and run them on my laptop, these are the results I get:
$ gcc -std=c99 mandelbrot.c -o mandelb│$ javac MandelbrotBW.java rot $(pkg-config --cflags --libs glib-│$ 2.0 gtk+-2.0) │$ $ for _ in $(seq 5) ; do /usr/bin/time│$ for _ in $(seq 5) ; do /usr/bin/time -f -f %e ./mandelbrot; done │ %e java MandelbrotBW; done 4.81 │4.57 4.79 │4.56 4.79 │4.56 4.79 │4.62 4.79 │4.54
Whoa, even despite the abstraction layers, the Java version is faster than C!
This is possible because Java Virtual Machines are not purely interpreters; they compile Java’s platform independent bytecode to native code “just in time,” while it’s being run. In the end the CPU is running native code to do pretty much the same thing, so there’s no reason why Java code should be significantly slower than equivalent C code.
But that’s not the end of the story. When I compiled the C version above I did not enable any compiler optimizations. Let’s see what happens when I do that:
$ gcc -O3 -std=c99 mandelbrot.c -o mandelbrot $(pkg-config --cflags --libs glib -2.0 gtk+-2.0) $ for _ in $(seq 5) ; do /usr/bin/time -f %e ./mandelbrot; done 2.62 2.62 2.62 2.61 2.62
And now the C version is faster by a large margin. Apparently the optimizations made by GCC are really good. Do we conclude that C is faster than Java?
It depends. We’re not really comparing the performance of one language to another, but the performance of code generated by different compilers. It would be interesting to have a look at the assembler code generated by GCC and by Hotspot, and see how they compare. It may be possible to tune the Java source so that Hotspot will generate code similar to what is produced by GCC, and use a more direct method of setting pixels than the
setRGB method. But would it then be a fair comparison?
From experience, programs written in Java tend to feel more sluggish than C programs. There are many things that might cause this: the startup time of the JVM, the time it takes for the JIT to kick in, garbage collector pauses, and the high overhead of native library calls, to name a few. The worst performance killer in my opinion is the general disregard for anything “low level” and the tendency of adding indirections and abstraction layers where none are needed, leading to complicated designs and bloated implementations. In comparison, C programmers tend to pay more attention to optimization, and their programs tend to use simple, flat structures, which is optimal for current CPUs. This doesn’t mean it’s impossible to write Java programs that perform as well as the equivalent C program.