Many Java applications have serious performance problems that can all be traced to one source - the use of double buffered graphics. Double buffered graphics is the trick of drawing into a off-screen buffer in main memory, and then copying the final image from the off-screen buffer to the screen. This trick has become part of the folklore of Java programming. How can you do flicker-free screen updates? The usual answer - use double buffered graphics. This is unfortunate. Double-buffered graphics are a massive performance hit for Java applications.
I suspect the answer to IsJavaSlow revolves mainly around this one issue.
What is worse is that Swing uses double buffering by default. One of my favorite applications (JBuilder) suffers from relatively poor GUI performance apparently for this exact reason.
Briefly the problems are:
Increased use of main memory
The buffer in main memory must be of size to match the window on the screen.
On my screen that buffer is 7680000 bytes (1600x1200 pixels at 32 bits/pixel).
A lot of memory to waste, for something you don't really need :).
You can see this empirically by:
Increased load on the main CPU
On any remotely modern PC the graphics chip on your video card is far faster than main CPU at doing graphic operations.
This is a triple hit as:
Meanwhile we're using that fancy super-intelligent video card as a dumb frame buffer.
Increased load on the system bus
One of the innovations in early versions of MicrosoftWindows was to send a stream of graphics drawing instructions to intelligent video cards, rather than raw bits. This cut load on the CPU and reduced the traffic across bus (remember not that long ago video cards were plugged into an ISA slot :).
We're setting the clock back to 1990 by using the video card as a dumb frame buffer. Each and every full-screen update copies the entire screen buffer from main memory to the video card. On my screen that's roughly 7 megabytes copied across the bus every time I scroll down one line in JBuilder's text editor.
Undetectable inefficient code
Writing good efficient GUI display update code is not always easy. You need to watch that you draw each part of the screen to be updated once and only once.
One trick I've used is to change all erase/area fill operations to use a distinct color, and turn the CPU clock way down. Watching your application run in slow motion is a wonderful way catch inefficient screen updates.
With double buffered graphics you can't see redundant updates, so it is far too easy for horribly inefficient code to creep into an application.
The irony in all this is I originally expected Java applications to be very little different in GUI performance from native applications. After all if most of the heavy processing was done by the video card, any slower execution of Java relative to native code would be hard to notice.
I still believe that this should be true. The main problem is not with Java itself, but rather with the implementation of the standard graphics toolkits.
There are cases where double buffered graphics are an appropriate solution. Small animations (like animated buttons) are harmless and can be nicely implemented using double-buffered graphics. What you do not want to do is end up using double buffered graphics for large parts of the screen.
So what are some alternatives we might use instead? And how did this pattern become so popular if it is so inefficient? (No criticism implied, just curious. I can see that double buffering might not be the SimplestThingThatCouldPossiblyWork.)
Any X-based double-buffered implementation works fast enough, even with the client-server architecture imposed upon you. It works even better on some Windows programs that support the X protocol. I've also seen the performance hit from double-buffering with Java. -- ScottJohnston
Good question! Next time I write a client-side Java GUI some experimentation will be in order. All my work lately is server-side Java and generated HTML.
I understand you can turn off double-buffering in Swing. How well Swing works with double-buffering off and whether a Swing application can be made to paint efficiently, these are questions to which I would love to have an answer.
I can't say anything about X Window Java performance (aside from having seen some interesting things in the SunJavaBugParade? :). If double-buffering is forced you are taking a huge notch out of the available memory and CPU. If your X installation makes full use of your video card processor you should see markedly slower performance from double-buffering. Conversely if your X installation does not make use of your video card processor you might not see a difference in performance.
How about this?: http://www.geocities.com/SiliconValley/Vista/1962/DoubleBufferedCanvas.java
This looks like an example implementation of double buffering. You can find many examples of this technique in Java books and anywhere Java questions are answered. In small use this is a useful technique. In large use this is what not to do :). -- PrestonBannister
I simply can't resist the FlameBait unintentionally presented here ... the reason why double buffering is so prevalent within Java is that the java community blindly pursued the portability myth while ignoring the experience of the Smalltalk community that already burned itself on this stuff.
DoubleBufferedGraphicsInJava is a degenerate solution to an overconstrained problem. Here are some aspects to contemplate:
Guess I am having trouble connecting this section with the initial topic. Double-buffered graphics is a something done in application Java code, and more recently built in to Swing. I don't think there is anything in the lower level Java platform-specific code or AWT that requires the use of double-buffering. Rather this is an unfortunate common practice. -- PrestonBannister
Well, it isn't that its exactly "required" -- the point I was trying to make (perhaps unsuccessfully) is that the overall architecture of the Java environment (language, VM, platforms, & culture) makes it so difficult to accomplish "real" graphics support that DoubleBufferedGraphicsInJava emerges as the best that's practical. -- TomStambaugh
Since the not so recent version 1.3, the Java 2 SDK for Windows uses DirectX rendering; it can be switched off and has some bugs that you can find in the Java Bug Parade. Also, JComponent has setDoubleBuffered() to control double buffering for each widget (and isDoubleBuffered() for querying). Maybe setDoubleBuffered() does nothing in common implementations. So I don't think Sun can do much better. -- LorenzoGatti
Double buffered graphics should be no slower in Java GUIs than in GUIs written in any language. Java should allocate off-screen bitmaps on the graphics hardware and then render onto them using the native API calls that are wrapped in Graphics objects in order to take advantage of hardware acceleration where possible. In fact, this is what the AWT did in Java 1.0 and 1.1 and double-buffering was as useful in the AWT as in any other toolkit.
However Java 1.2 introduced the Java2D API that provides antialiasing, sub-pixel coordinates and general affine transformations applied to each drawing operation, even image blits. Furthermore, these operations are not supported by X11 or Windows. Drawing operations have to be composited in main memory before being flushed to the screen. This results in image blits being much slower than previous versions of the AWT and so reduces the performance of double buffering.
Hopefully future versions of Java 2D will use OpenGL or the Render extension that has been added to XFree86, both of which can support the features of the Java2D API and will allow hardware acceleration.
Ah, I see. So not only does Java offer only double buffered graphics, but even THAT is slow and badly engineered! This reaffirms my contention above: the Java creators, in their arrogance, ignored the experience of those who went before them and we all now pay the price. OpenGL, if it can be supported by JNI underneath it, would be great. Don't hold your breath. -- TomStambaugh
Java does not necessarily offer only double buffered graphics - the Java2D library could draw into a bitmap that corresponded to a window in the display memory and is mapped into main memory and then shared between the display server and the Java process as a shared memory object. I don't know if this is how Java2D is implemented, but it would make sense.
On the other hand, this doesn't stop Java2D from performing all rendering and compositing operations itself - as it seems to do on both Windows and X11 -- or slow double buffering by applying the same compositing rules to image blits.
Using something like OpenGL isn't about "drawing into a bitmap" - it is about passing callbacks and displays into rendering hardware that decides when and how to actually manipulate the pixels. Given the rest of the Java architecture (for example, its casual assumption of pixel graphics, its abysmal numeric and collection support, and the clumsy way it does closures), I think effective JNI support for OpenGL in any portable (across architectures) way will be nearly impossible. -- TomStambaugh
There are several implementations of JNI code that links OpenGL and Java and the OpenGL ARB is standardizing an OpenGL API for Java. OpenGL is a cross-platform C library, as is JNI. OpenGL abstracts the program away from the hardware, as does Java.
It's possible to get good <cough, read 286 speed> performance on any of the platforms, but it requires a lot of black-magic i.e. the defaults won't work. A double-buffered approach will work if it is a display-synchronous buffer, otherwise flushing to the screen on each update event is pathetically slow. I'm having to recreate the base physical architecture at a higher level to get the result. Shows the shameful ImpedanceMismatch of Java. Maybe TowerJ can rescue me.
Quick update, looks like v 1.4 might include some relief, but then they've been promising the fast I/O package for three releases. -- RichardHenderson.
I find it shameful that Java got this so badly wrong (twice!). How can the Java platform ignore how well the Windows platform handles this, even without DirectX? It's not the implementors - Windows adopts the same strategy - it's the platform!
Also see: DoubleBuffer, CategoryGraphicsPattern