Neil's Place

October 24, 2009

Command Updating Performance

Filed under: Uncategorized — enndeakin @ 2:13 pm

This week, I’ve been investigating how to improve command handling in XUL, both in terms of manner they’re used and particularly, performance. Command handling is what deals with performing actions in response to menu or keypresses. There are several types of commands. The first are those like cut and paste that typically appear on menus, where the bulk of the handling deals with ensuring that the menus, toolbar buttons and actions are enabled and disabled at the right time. The second are input field cursor and selection commands (such as move to next word, page down, etc). Mozilla cheats a bit and doesn’t check if they should be enabled as they don’t normally have UI that shows this. The third are complex edit commands such as bold, font changes and so forth. These are similar to the first set really. As an aside, it would be quite nice to be handle all of these in a similar fashion.

For whatever reason, most of the handling is done in C++ with a small amount of code done in script. In many cases, though the script is unnecessary. For example, a typical cut command looks like the following:

<command id=”cmd_cut” oncommand=”goDoCommand(‘cmd_cut’)”/>

This is quite pointless as the act of ‘do’ing the command should just be the default behaviour.

Updating command state is another issue and one which can be quite slow. In fact, a workaround was added just to improve performance due to this. Updating command state refers to the process of updating the UI when the focus or selection changes to reflect which commands are now enabled. For example, a cut operation is only valid when there is text selected.

Here’s an overview of how the update process breaks down in time used:

Fire commandupdate event at updaters: 17%
Retrieve the topmost window: 14%
Retrieve the command dispatcher: 8%
Retrieve the right controller: 33%
Checking if the command is enabled: 15%
Set disabled attribute on command elements: 13%

I was expecting the latter two steps to be the bulk of the time used, but this wasn’t the case. This was a good sign since the other steps are easier to optimize away. One particular issue is that, except for the first step, the remaining steps occur again and again for every command that needs to be updated, which includes additional time due to calling into Javascript and back several times.

Instead, by optimizing to perform the repeated steps only once, as well as not calling into Javascript unnecessarily we can reduce the amount of time taken up. Most of the time, the script just calls back to update each command in the same way every time much like goDoCommand is called in the earlier example, so this is also a bit silly.

I created a simple performance test which updates the typical eight edit-related commands one thousand times. With current code, the test took over 625 milliseconds to complete (which means about half a millisecond each). This isn’t much individually, but might be noticeable if lots of commands were being updated.

I created a simple implementation of the changes described above, still a work in progress, and have reduced this to about 60 milliseconds to update one thousand times. This is a speedup of over 10 times.

I have also been experimenting with caching the list of commands and their disabled state, rather than setting a disabled attribute on the command elements. This can improve the time to 40 milliseconds. One advantage to this is that it could mean that the command element isn’t needed for many commands — if we cache the disabled state internally, in many cases we don’t really need the command element any more.

In many cases, the UI for commands such as undo isn’t actually visible until the Edit menu is opened. Using some smart caching, we can make the work of updating these commands only occur when the menu is opened. This already happens to some degree, but much additional and extraneous work is done upfront rather than later.

Next, I’m going to investigate some of the other types of commands and further see how a smart command caching mechanism might work.

Advertisements

4 Comments »

  1. We get to blame the Mac for most of this, since its menu code needs the menuitem to accurately reflect the disabled state of the attached key. If this could be fixed then I’d happily move a ton of menuitem updates from commandupdaters to popupshowing events, and make the keys all use goDoCommand directly.

    As for goDoCommand being the default behaviour, don’t elements get confused if they can’t find an oncommand attribute on their key or command? At least, they used to; I seem to remember trying and failing to write .

    Comment by Neil Rashbrook — October 24, 2009 @ 5:22 pm

  2. One possible downside of caching until the menu opens is that menu opening is already kind of slow on Windows CE devices (eg Tegra). For example, click a menubar item to open the menu, and move the mouse side-to-side over other menubar items… The menu updates significantly lag behind the mouse movement.

    Comment by Justin Dolske — October 24, 2009 @ 10:05 pm

  3. So, does from 625ms to 40ms mean a speedup in UI responsiveness by a factor of over 15?!

    Comment by Mardeg — October 24, 2009 @ 10:06 pm

  4. What kind of Mac are you using? I’m running a G5 PPC and wonder what the perf stats are for this on my machine. Any chance you could send me the test to run? I can do builds/etc.

    Comment by Chris Andrichak — November 8, 2009 @ 2:42 pm


RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Blog at WordPress.com.

%d bloggers like this: