swing slowness.. myth?

well. topic seems to be done for trolling, but be patient. i don’t think it is.
Here is why i started it.
This morning, i was documenting my code using sunOne’s autocomment tool. (excellent tool, by the way). You might have noticed that it has a vertical jsplitpane included. When i moved it to have a better work area, i felt i was back on a CPC (or MO5, for those that still remember that … thing…), or something equivalent… One update a second, at most. I thought “well, it seems that rants on JGO are in truth… that is damn slow and unacceptable”. Then, i went back to my code, sad because of the use i wanted to make of Swing, and its bad performance.
After some time, questions came to my mind, as bees come to flowers. I could not resist: i opened the task manager and saw that in fact i could not distinguish the cpu power used by the redraw from the noise of the OS. In fact, that slooooow redraw was not slow at all, there was just a refresh every second.
Then i tested many swing apps, an eye on the windows, the other on the cpu graph. while just moving the mouse takes 6 to 14% (!!!) for the kernel no swing operation took more than 50% of the whole CPU (including mouse movements).
You imagine i was quite puzzled by this, maybe just as you are now.
So, there are many things i’m wondering…
Is there a swing app that goes slow and uses all the CPU?
Is the swing team using wrong techniques to delay refreshes, or things hidden in the wood that dramatically change the refresh rates? shouldn’t refresh rates be prefered to increase responsiveness?
When some of us are talking about something like “perceived performance”, i feel that we got catched by a trap with this. I percieve slowness, but can’t percieve CPU use… what’s wrong?

Swing’s slowness comes in four flavours, in no particular order:

  1. Memory footprint. Swing gobbles a large chunk of RAM. Unfortunately it double buffers everything as well by default and unbelievably this means allocating a chunk of RAM as big as your biggest window - agh! When memory is tight we see a lot of swapping, and this kills performance for many users of ordinary machines.

  2. Classloading. Swing relies on a fantastically large number of classes because of its terribly clever look-and-feel. Classloading is one of the slowest operations a JVM does. Not only that but it will tend to wait a little while before compiling bits of them too.

  3. Garbage. Swing produces much garbage while it runs. It’s all very well to claim this has been cured by -Xincgc and -Xconcgc but in reality these two flags slow the whole application down by 20% and make it feel worse than the occasional long pause.

  4. Update delay. Swing is clever and double buffers, and batches repaints (is it every 100ms? Can’t remember). This leads to the perceived sluggishness of the display updating, when native apps are drawn direct and unbuffered to the display using hardware acceleration. I’m not sure either if the AWT uses Windows’ accelerated GDI calls to draw lines and boxes on its own in-memory surfaces either; many 2D cards optimised GDI calls to draw lines direct to video RAM which is why the safe mode in Win32 feels so slow. Sun have made the mistake of using DirectX - a game technology - to render normal GUIs. In this way they lose all the 2D GDI acceleration functions and still they have to blit everything from system RAM to the video buffer.

In fact, start Windows up in VGA safe mode, and note how the GUI repaints itself. Feel familiar? It’s just like a single-buffered Swing application…

Cas :slight_smile:

Hi, Cas.

[quote] 1. Memory footprint
[/quote]
I don’t see how this slows performance, when it increases them. Can you explain further?
added later: of course i’ve got plenty free memory and no swapping occured while testing, so it obviously did not apply to this test.
For the moment, i’ll retain only three of the four points.

[quote] 2. Classloading.
[/quote]
I was talking about an application already loaded, and of course, the ‘stretch that window’/‘move the splitpane’ operations were done long enough for a human to compile the bytecode himself, by hand and drunk. :slight_smile:
Thus, classloading does not count as a slowness factor in this case.
So, i’ll retain only two points for the moment.

[quote] 3. Garbage
[/quote]
Please compile this file below, and start it using the -Xloggc flag.
Here is an serie of GC that occured within the same second when i was resizing the palette internal window.

17.215: [GC 1735K->1218K(1984K), 0.0004841 secs]
17.3926: [GC 1730K->1226K(1984K), 0.0007386 secs]
17.6234: [GC 1738K->1218K(1984K), 0.0010415 secs]
17.8802: [GC 1730K->1228K(1984K), 0.0008227 secs]

While i agree that there is a big amount of garbage generated, during that second, collecting only took 32ms, so about 3% of the running time. (if i made no mistakes)
Okay, for a game that might cause problems, but for a swing interface, this is not noticeable.
Do you care if i don’t retain that point?

/*
 * SwingRefresh.java
 *
 * Created on 11 janvier 2003, 12:07
 */

//package com.iolabs.bench.swing;

/**
 *
 * @author  pepe
 */
public class SwingRefresh extends javax.swing.JFrame
{
      
      /** Creates new form SwingRefresh */
      public SwingRefresh()
      {
            initComponents();
      }
      
      /** This method is called from within the constructor to
       * initialize the form.
       * WARNING: Do NOT modify this code. The content of this method is
       * always regenerated by the Form Editor.
       */
      private void initComponents()
      {
            jSplitPane1 = new javax.swing.JSplitPane();
            jTable1 = new javax.swing.JTable();
            jDesktopPane1 = new javax.swing.JDesktopPane();
            jInternalFrame1 = new javax.swing.JInternalFrame();
            jTable2 = new javax.swing.JTable();
            jInternalFrame2 = new javax.swing.JInternalFrame();
            jColorChooser1 = new javax.swing.JColorChooser();
            jPanel1 = new javax.swing.JPanel();
            jLabel1 = new javax.swing.JLabel();
            memLabel = new javax.swing.JLabel();
            
            addWindowListener(new java.awt.event.WindowAdapter()
            {
                  public void windowClosing(java.awt.event.WindowEvent evt)
                  {
                        exitForm(evt);
                  }
            });
            
            jSplitPane1.setDividerLocation(300);
            jSplitPane1.setContinuousLayout(true);
            jTable1.setModel(new javax.swing.table.DefaultTableModel(
            new Object [][]
            {
                  {null, null, null, null},
                  {null, null, null, null},
                  {null, null, null, null},
                  {null, null, null, null}
            },
            new String []
            {
                  "Title 1", "Title 2", "Title 3", "Title 4"
            }
            ));
            jSplitPane1.setLeftComponent(jTable1);
            
            jInternalFrame1.setResizable(true);
            jInternalFrame1.setVisible(true);
            jTable2.setModel(new javax.swing.table.DefaultTableModel(
            new Object [][]
            {
                  {null, null, null, null},
                  {null, null, null, null},
                  {null, null, null, null},
                  {null, null, null, null}
            },
            new String []
            {
                  "Title 1", "Title 2", "Title 3", "Title 4"
            }
            ));
            jInternalFrame1.getContentPane().add(jTable2, java.awt.BorderLayout.CENTER);
            
            jInternalFrame1.setBounds(40, 50, 270, 250);
            jDesktopPane1.add(jInternalFrame1, javax.swing.JLayeredPane.DEFAULT_LAYER);
            
            jInternalFrame2.setResizable(true);
            jInternalFrame2.setVisible(true);
            jInternalFrame2.getContentPane().add(jColorChooser1, java.awt.BorderLayout.CENTER);
            
            jInternalFrame2.setBounds(30, 350, 330, 230);
            jDesktopPane1.add(jInternalFrame2, javax.swing.JLayeredPane.DEFAULT_LAYER);
            
            jSplitPane1.setRightComponent(jDesktopPane1);
            
            getContentPane().add(jSplitPane1, java.awt.BorderLayout.CENTER);
            
            jLabel1.setText("Memory used:");
            jPanel1.add(jLabel1);
            
            memLabel.setText(" ");
            jPanel1.add(memLabel);
            
            getContentPane().add(jPanel1, java.awt.BorderLayout.SOUTH);
            
            pack();
            java.awt.Dimension screenSize = java.awt.Toolkit.getDefaultToolkit().getScreenSize();
            setSize(new java.awt.Dimension(675, 621));
            setLocation((screenSize.width-675)/2,(screenSize.height-621)/2);
      }
      
      /** Exit the Application */
      private void exitForm(java.awt.event.WindowEvent evt)
      {
            System.exit(0);
      }
      
      /**
       * @param args the command line arguments
       */
      public static void main(String args[])
      {
            SwingRefresh sr = new SwingRefresh();
            sr.show();
            MemTick mt = new MemTick(sr.memLabel);
            mt.start();
      }
      
      
      // Variables declaration - do not modify
      private javax.swing.JTable jTable2;
      protected javax.swing.JLabel jLabel1;
      protected javax.swing.JLabel memLabel;
      private javax.swing.JSplitPane jSplitPane1;
      private javax.swing.JInternalFrame jInternalFrame2;
      private javax.swing.JColorChooser jColorChooser1;
      private javax.swing.JPanel jPanel1;
      private javax.swing.JInternalFrame jInternalFrame1;
      private javax.swing.JDesktopPane jDesktopPane1;
      private javax.swing.JTable jTable1;
      // End of variables declaration
      
      
      
}

class MemTick extends java.lang.Thread
{
      javax.swing.JLabel label ;
      Runtime r;
      
      public MemTick( javax.swing.JLabel label )
      {
            this.label = label;
            r = Runtime.getRuntime();
      }
      
      public void run()
      {
            try
            {
                  while (true)
                  {
                        sleep(1000);
                        label.setText( Long.toString( (r.totalMemory() - r.freeMemory())/1024 ) );
                  }
            }
            catch ( java.lang.InterruptedException e )
            {
            }
      }
}

[quote] 4. Update delay
[/quote]
This is where i really think that the problem is, and unfortunatly i can’t do anything to test or change.
Any idea?

Actually, and due to the reasons i explained in this post, the fact that swing does slow/rare refreshes and does not use all CPU power is unexplained, and unproven.

This is pure speculation on my part so don’t flame too hard if you know this to be false.

I think the slowness of SunOne Studio/NetBeans is that they do too much with the Swing event thread. You can’t process other events if you’re taking a long time on this one.

I don’t think it’s memory footprint because after ~20 minutes of use IDEA and SunOne/NetBeans report using about the same amount of memory but IDEA feels more snappy.

SunOne/NetBeans does load an ungodly number of classes and they extensivly use late binding which has it’s own set of perception/perception problems. I also think there is a lot of inderction going on considering how NetBeans is really one big plugin framework.

Speculation is everywhere, now… no problem.
So, if we eliminate netbeans and sequels, what remains?
Is there a swing app with slow interface that takes 100% of the processor when refreshing, with slowness only due to swing?
In the lame snippet i’ve posted, swing takes the whole CPU (with around 40% being kernel) to refresh, but it is NOT slow at all. For example, the resize of the palette, which is not a simple interface, is very fast and perfectly follows my cursor, even if i try to make it mad, and if GC comes a bit over 4 times a second.

Pepe, I was addressing the feel of slowness from start to finish more than just what it feels like after it’s warmed up.

If you’ve got plenty of RAM, a reasonably speedy processor, run your application for a while, and get used to the update delay, you find Swing feels like an unaccelerated GDI driver, which feels decidedly cranky next to the accelerated ones we’re used to.

But, if like most people you’ve only got about 32-64Mb RAM, an average processor of 200-350MHz or so, and just want to fire up an application and use it a bit and then close it, Swing’s just going to feel like swimming through treacle - I think that’s where the real performance perceptions actually come from from real-world users. So you might want to retain those points, or put them on the backburner.

As for why Swing doesn’t use 100% CPU but still feels sluggish - that’s precisely because it batches some events up instead of acting on each on immediately. It’s a more efficient way of using CPU but of course ultimately feels much slower. I think it may have been the wrong approach for a client application; it’s a serverside technique really.

Edit:
hey, have you checked out the RepaintManager?
or perhaps it’s possible to hijack the Swing implementation by inserting a couple of different classes in the bootclasspath and get rid of the update delay?

Cas :slight_smile:

[quote] But, if like most people you’ve only got about 32-64Mb RAM, an average processor of 200-350MHz or so, and just want to fire up an application and use it a bit and then close it, Swing’s just going to feel like swimming through treacle
[/quote]
Mhh. okay. So is the burden due to swing or will it be the same for any GUI engine with similar functionalities?
Is swing really to blame, or would people blame any other system following the same path?
I think that maybe the second possibility should be true.

on the low end (and common) side of the market, i agree that it might be a problem. But would the same problems appear with an other engine ? Not really sure… Do we have to blame swing for being ?
I have spare PII 300 and motherboard that i will assemble rapidly to test the snippet

One last word about the memory use, and buffers. I don’t think that being native will change anything. you graphic card certainly has your windows as bitmaps in case of window translation, for example. Thus swing is certainly not the only one to keep memory for the windows displayed. I’m sure that windows does also that, but does not count that memory as if it belongs to the program.

I’m quite convinced that the slowness in swing comes from the components, thus leading into the double buffering issue. If you have an application, which uses fullscreen and has ignored repaint on all the components and then paintcomponents() is called within the rendering thread(may it be swing event thread or swing rendering thread which is running the runnable) performance is increased signifigantly. However, this leads to a problem where all the components must be queued up in order to not loose the track of what the user is doing, but this is noting to worry about unless user performs big memory/processor consuming operaitions.

If the panes are disabled and bufferstrategy is used, I’ve found quite humongous speed increasements.

I have no first has knowledge of this, but is swing components accelerated? At times it seems like they are not, especially when using bufferstrategy, when the problem is gone and at times when they are just slow.

Swing is definately slow. It is a fact, not a myth, If I can see the delay, then it is slow.

[quote]If I can see the delay, then it is slow.
[/quote]
Those are two very different things.
something that comes late does not mean that it came slowly. It can also mean that it started its way to you later than you think it started.
Would you accept me saying that you are slow, just because your post came some hours after the start of the thread? Certainly not.
In the same point of view, is swing slow, or does it suffer from the event regrouping?
That is the whole thing that needs to be found.
If it is slow, well, maybe it is not done the correct way, and there won’t be anything we will really be able to do. if it’s because of event regrouping, changing the strategy could lead to really different performances.

TANSTAAFL

Swing is a highly flexible GUI lib. I ensures a GUI can look anyway you want it to look, is extendible like hell and in the end, very easy to use. Compare it to Win32.
This of course demands a large number of classes in OO (‘single responsibility’), which in turn produces garbage of course.

Additionally it relies on painting with one of the most powerful, platform independant 2D apis around. Look for transparency, antialiasing, free transforms, internationalized font handling, … at other APIs. There barely is one. So don’t blame Swing when Java2D renders a bit slow…

This of course is not for free! But it is a GUI lib. Means it deals with user interactions. From a GUI point of view living in a GHz machine, a user nearly doesn’t do anything. A mouse-move event from time to time, that’s most of it. So the performance measurable in a microbenchmark doesn’t say anything about the quality of a GUI lib.

NetBeans is another topic. NetBeans, so to say, is a second-order GUI lib. So for everything happening in there, there are dozens, hundreds or even thousends of Swing actions taking place. I agree that NetBeans is very far from being fast, but it at the same time is one of the most extendible and flexibles frameworks on earth. Reduce extendability, specialize more on a certain topic and things speed up. So IDEA is faster, and jEdit is even faster.

So it IS a myth to say that Swing is slow. It is fast for what it does.

Sometimes suprisingly fast. In my company, when we evaluated which technology to use for GUI development, I tested Swing to display large amounts of biological data with respect to high display quality. I needed transparency, I liked to have antialiasing.
First then, I found Java2D to be the ONLY lib I could manage to do that at all. It for sure was my fault not knowing enough about GDI to enable transparency and antialiasing - but easy-of-use also is one point why we are here. I was really surprised how FAST theses things got rendered!!
Ok, you cannot do it 60 times a second. But it’s a GUI, I only need to do it once when the user clicks on the scrollbar!

At the end of the day I felt that Swing was fast enough to make a GUI with it. Meanwhile our frontend evolved into a NetBeans-like framework and we do quite a lot of non-standard GUI. We have a LOT of problems, but performance never has been one of them.

Sure, if we’d headed for natively rendering COM controls thoses days, our GUI might be quicker today. But for sure it would have far less features!!!

If you need to do a GUI, use Swing. It’s really very, very valuable!

Wow, that was long…

I don’t think so. I can get over 200fps in a swing window by simply calling the asynchronous repaint() on my display-Component (which extends JComponent and only uses paintComponent()). The FPS is measured in the paintComponent() and the calling class and is most time the same value unless cpu usage get too high, then several repaints() get packed into one paintComponent().

I’ve always said that it is a myth… I program most stuff exclusively with Swing. In some cases the percieved performance is greater than that of the equivalent native app. Some things are slower, but never “too slow”

Painting drop down menus for instance is generally a bit slower… (particularily with icons) but I don’t care because they always paint plenty fast enough.

I have a native app and a Swing app that are basically the same. A JSplitPane with continuous layout tends to perform better than the native app doing the same thing. The Java version also looks better than the native app because it does not flicker while painting in this case.

I think the main slowdowns with Swing based apps are caused by not changing the model in the model,view, controller architecture when the default model is not suitable. For example the default model may fire off too many property changed notifications or other such events when being manipulated by your code… (eg. inserting many items into a list).

I did make an asteroids clone with Swing a LONG time ago and it had reasonable performance… it was the first ever JAva game I did though and I could improve the performance a lot now (and still use Swing).

thread resurrection!

Fair enough, Swing’s quick enough for a few apps (well, is it bollocks, I’ve just written an MDI application and the mere act of opening the main frame with a menu on it takes a whole second on a 1.2 P3 but that’s by the by).

But what about for games?

Specifically, what about active rendering?

Can a GUI be drawn fast enough in realtime without chewing up all the processor time? We should know! Who’s done any active rendering with Swing successfully?

Cas :slight_smile:

If you’re doing the active rendering in just one JPanel, theres not much todo for Swing and I see no reason for problem.

I once tried to have an action game using all nice Swing mechanisms in it. The background was a JPanel, the ships have been JPanels, the shots … and so on, all encapsulated in a JScrollPane. One timer updating all the content with 30Hz. A ship moving meant to call JPanel#setPosition() which in turn causes a repaint for the dirty area and so on.
Worked fine with 1 ship or 2, but failed for 10 or more. You repaint errors and such.

Swing is not designed for that. I think a pacman might work this way, asteroids might already fail. But if you CAN use it, is save a lot of code (dirty rect management, repainting,…) and gives you the power of Java2D with all it’s possible effect, transforms, and so on. See the Java2D demo - it shows activly rendered context.

[quote]You repaint errors and such.
[/quote]
Maybe due to some calls to swing not in the refresh thread?
Woud you mind sharing that game? i would be very interested in seeing that…

[quote]See the Java2D demo - it shows activly rendered context.
[/quote]
And it does it well, IMHO.

only problem with Java2D, is that 95% of the api is useless if you want fast code. (AffineTransform, AlphaComposite, Clipping, GradientPaint, etc etc)

Until all this code gets implemented in a hardware acceleratable way (and fixed, so it doesn’t create large amounts of garbage) its useless for anything but tech demos. :-/

Not fast?? well. that’s not what i saw when testing the J2D/J3D association for a 3d painter.
I could paint rotated, scaled, filtered, subpixel positionned, 256256 transparent brushes and transfer them as parts of 10241024 texture for java3d with around 50 (!!!) updates per second. At this time, it was on a biPII 350 and a geforce1… I can’t imagine what i’ll do on actual machines…
That was done directly on the polygons, and i could see the update in realtime on the object while painting.
Believe me, that is NOT slow. Certainly not…

I don’t have UV painting for the moment, but at the time i tested, i was 1000 times faster than Maya’s painter.
How proud i was. ;D

and that was using 1.3.0.

I’m sorry, never made a game of it. Checked it out, sucked, thrown away…

I suppose it wasn’t a threading issue but just to many changes too fast. Imagine you have 10 mice and push 10 windows around at the same time…

[quote]only problem with Java2D, is that 95% of the api is useless if you want fast code. (AffineTransform, AlphaComposite, Clipping, GradientPaint, etc etc)
[/quote]
Hm, if you don’t need these things, yes, they are useless and Java2D is not the world champion with drawImage(). Never user these VolatileImage things. Can’t they be blitted very fast?

OTOH, I don’t think they can perform high-quality graphics operations like AffineTransform, AlphaComposite, Clipping, GradientPaint, etc etc

Why not using them for a game with stunning effects (although nowadays these things are typically performed in 3D also in 2D games)?
Not every game needs to go with 60FPS!