|
|
||
Tom Ball's BlogApril 2005 ArchivesOptimize a Swing App by Slowing It DownPosted by tball on April 11, 2005 at 04:54 PM | Permalink | Comments (4)Swing is slow, right? That seems to be a common perception which fast Swing apps like LimeWire seem unable to shake. Even the Jackpot GUI app is sluggish these days, and we all know it cannot be that my code is at fault, right? I think the real problem is that yours and my computers are too fast, or at least too fast relative to our customers' systems. Because software developers and quality testers tend to have very fast systems so they can multi-task effectively, an unfortunate side-effect is that they tend to run their applications faster than their customers do. As a result, it's easy to overlook inefficient and redundant GUI operations as long as the result looks and feels okay on a fast system. Slow your system down enough, however, and these problems leap out at you, demanding attention. One easy way to slow down a Swing application is to turn on DebugGraphics functionality. Although DebugGraphics was designed to display potential problems with display painting and layout, it has a nice side-effect of pausing after each paint operation (otherwise you wouldn't be able to see it work). Here is an example of how one might use this:
private static final boolean debugGraphics = true;
...
if (debugGraphics) {
DebugGraphics.setFlashTime(100); // 100 ms. pause
DebugGraphics.setFlashCount(1); // only pause once per paint
DebugGraphics.setFlashColor(new Color(0,0,0,0)); // no color
// turn off double-buffering, or this gets optimized away!
RepaintManager.currentManager(myComponent).setDoubleBufferingEnabled(false);
myComponent.setDebugGraphicsOptions(DebugGraphics.FLASH_OPTION);
}
The first time you run this code, do so in the debugger. If it appears to hang, suspend the app and see where it is stuck. When I first ran it on my app, I had turned DebugGraphics on for my whole component tree -- DebugGraphics was pausing after each bump was drawn on my JSplitPane slider! Not very useful... Once I only turned debugging on for my main panes, it became painfully obvious that some operations of my apps GUI operations were redundant. My app has a JTable which is supposed to select a JTree node when a row is clicked, but selection was happening multiple times. Setting a breakpoint on my ListSelectionListener showed I needed to first check ListSelectionEvent.getValueIsAdjusting() and only handle the event if its false; failing to do so was causing a new list selection event to be fired for each entry in the tree path for that node (maybe Swing wasn't the problem after all!). These multiple selection events were not that noticeable on my fast system, but after fixing this and other similar problems, the GUI is much perkier. I am going to leave this code enabled in my app for a few more days. The slow display will irritate me a lot, and so I'll just have to make it faster some other way. Wish me luck! Exceptional DebuggingPosted by tball on April 04, 2005 at 03:36 PM | Permalink | Comments (9)Exceptions (and their stacktraces) are one of the features that first attracted me to Java back in early 1995; C++ obviously had exceptions, but I never worked with any C++ projects that used them as effectively as the Java core classes did. I find well-designed exceptions to be a significant productivity booster. My first project on the JDK team was developing the debugger API, and I think it was the first debugger where you could set a breakpoint that tripped whenever a specified exception type was created (please correct me if you know of earlier systems). The justification for exception breakpoints was simple: exceptions (should) indicate abnormal control flow in a program, and since we spend most of our time in debuggers figuring out why our programs are acting abnormally, there should be an easy way to halt whenever the program is indicating something worth investigating. But few engineers I've met use this feature, and it puzzles me a little. Most recently, I filed a NullPointerException problem which didn't log a stacktrace, and the assigned engineer first added logging code so he could get a stacktrace to see where the problem was, which he then spent a lot of time reading various source files to determine what could have caused it. If he had instead set an NullPointerException breakpoint, the debugger would have stopped at the offending statement with all of its local variable and stack information immediately available. This is much, much faster than running until a stacktrace prints, editing the code to add debug statements if the cause isn't obvious, and looping until the problem is figured out. Isn't impatience supposed to be one of the attributes of a good programmer? Exception breakpoints can be useful even when you aren't debugging exception failures, too. In NetBeans, I always have breakpoints set for NullPointerException and AssertionError (running with the -ea flag), to catch these conditions as soon as they rear their ugly heads. I have heard complaints that exception breakpoints are "too noisy" because they can trip in places you don't expect. I think this indicates a couple of potential design flaws:
| ||
|
|