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.
- Increased load on the main CPU.
- Increased load on the system bus.
- Undetectable inefficient code.
Let's take this apart...
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:
- Start an application with a very small window.
- Check the memory footprint.
- Upsize the window to full-screen.
- Check the memory footprint.
If you see the memory footprint jump by an amount computable from your screen size, then it's a pretty good bet the application is using double buffered graphics.
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:
- Graphic operations take longer to complete.
- Any concurrent processing will take longer to complete (contention for the CPU).
- Any following operations will take a hit as the CPU cache reloads (graphic operations blow out the cache).
and we're still not done :).
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.
is a degenerate solution to an overconstrained problem. Here are some aspects to contemplate:
- X-Windows makes it nearly impossible for the Java VM to get at the video hardware of the display server, X-Windows is the nearly universal display protocol of the UNIX world, and Java is still dominated by this backwater of UI technology. There was an "X" bandwagon once, and this abysmal disaster of a "graphics protocol" is its legacy.
- X provides several ways of getting high-performance access to the rendering hardware, such as shared memory extensions (MIT-SHM), the Direct Rendering Infrastructure (DRI) and the GLX extension for OpenGL. The presence of these is standard on particular platforms.
- There are virtually NO video hardware standards that are portable across architectures. This means that some form of JNI is required.
- What has hardware standards have to do with JNI? Java does not allow the program to make calls to the hardware at all for security reasons. Any access to the program's environment has to be made through JNI calls or features of the language implementation itself (threads and JVM). Therefore any graphics operations will have to be made through JNI calls.
- The Java language "architects" chose to ignore the experience of the Smalltalk community, specifically the Envy/Developer community, and precluded class extensions (groups of methods that can be added to an existing class, without changing its definition). This, in turn, precludes Envy-style subapplications, and therefore makes runtime platform-specific customization nearly impossible. Thus, in Java it is dramatically more difficult than in Smalltalk to write an abstract graphics layer that takes advantage of platform hardware. Java Interfaces might help, but nobody I know understands how.
- I don't understand this at all. There is a wealth of experience in building high-performance graphics APIs that support multiple types of graphics hardware. OpenGL is one example, DirectX is another. It's a small step from a procedural API to an API based on abstract interfaces.
- The java graphics environment is pixel based, making it nearly impossible to code genuinely portable graphical design. DisplayPostscript offered a better alternative a decade ago, but was crushed by the XWindows bandwagon. Once again, we now get to pay the piper. Try having a conversation with graphic designer who uses a vocabulary of "point size", "leading", "gutters", and "Pantone colors" - and then code *that* into your Java toolkit of choice.
- Java2D uses an affine transform to map program coordinates to device coordinates. AWT exposes the physical properties of the display. This allows a program to calculate the transform mapping points to pixels for its display surfaces. The java.awt.color package provides an extensible API for color models, allowing vendors (such as Pantone) to add additional color models as necessary. Java has good support for this kind of graphical application. However, it is this very support that makes double-buffering so slow.
Maybe someday somebody will actually SOLVE this problem. Right now, it makes me shudder to think of using Java to actually touch the screen for anything real. -- TomStambaugh
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.
Double buffered graphics should
be no slower in Java GUIs than in GUIs written in any language.
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.
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