Neil's Place

November 3, 2011

Window Reflows on Startup

Filed under: Mozilla — enndeakin @ 2:02 pm

I took a look at what happens on Firefox startup on Mac with a profile I’ve used for testing for a while. Specifically, how often and why the root element (<window>) in the browser window is asked to be laid out. Each line below is an attempt to lay out (reflow) the root element. A reflow can happen as a result of a number of different things such as new elements being inserted, images being loaded and so forth. What happens is that the document layout objects are marked as dirty as necessary, then at a future point, a reflow occurs. This allows a script to make a number of changes without having each change cause a full refresh. I’ve broken down each attempt to mark the root element (or some other reflow root) as dirty, separated by commas in each line. In the table below, a reflow occurs 15 times.

Action

Time to finish
Hidden Window

0.55ms

Main Window is parsed and laid out

14.95ms

Attribute changed, and loaded page (about:blank) is laid out

0.78ms

Search icon is set

0.35ms

Scrollbar attributes changed, toolbarspring is inserted, window’s load event fires and listeners called and finished

2.21.ms

Scrollbar attributes changed, tabview.png is set

0.34ms

folderDropArrow.png is set

0.27ms

browser.js delayedStartup() is called, splitter is inserted into urlbar

0.24ms

menupopup is appended to back button, delayedStartup() finishes, bookmarks toolbar handling begins

5.77ms

chrome://browser/skin/places/query.png is set

0.56ms

chrome://browser/skin/page-livemarks.png is set

0.39ms

chrome://global/skin/tree/folder.png is set

0.46ms

Call to flush frames

0.33ms

Bookmark icon is set

0.48ms

Bookmark icon is set

0.37ms

The last few images are all the assignment of the icons on the bookmarks toolbar. These don’t occur if the toolbar is hidden. In total, approximately 400 changes to an element’s attribute are made and 41 nodes are appended to the document after it is parsed.

Now let’s take a look at the mobile version, running on an Android tablet. Here, 24 reflows occur over the 2200ms startup time.

Action

Time to finish
Insert html element into main window?, insert html element into hidden window?, parse and lay out main window

7.43ms (also 183.7ms reading xbl)

Four dirty marks caused by scrollbar attribute changes

1.07ms

Remove text node (a <br> is appended to a <div> immediately following), append <documenttab>

2.55ms

Append <notificationbox>

0.78ms

Some attributes on <label> and <image> elements are changed, some <label> elements are removed

1.20ms

scrollbar.pageincrement is changed

0.28ms

SizeToContent is called

0.22ms

Unknown reason, but probably caused by bug 230959

0.94ms

slider.pageincrement is changed, attributes on <tablet> are changed

5.329ms

Three dirty marks caused by scrollbar attribute changes

1.65ms

Attribute changed on a <scrollbox>

1.19ms

Attribute changed on a <scrollbox>

0.57ms

Attributes left, width and height changed on a <vbox>

1.40ms

Two scrollbar related dirty marks, mode attribute changed on various things (no dirty mark), loading attribute on an <image>

1.88ms

mode attribute changed on various things (no dirty mark), loading attribute on an <image>

0.76ms

Some <image> elements are inserted and removed

0.72ms

Unknown reason, probably a flush caused by retrieving some layout information

0.53ms

Unknown reason, probably a flush caused by retrieving some layout information

0.68ms

Loaded page starts appearing, <toolbarbutton> label changed

2.27ms

Text node appended to page

0.51ms

Text node removed from page, height changed on an <hbox> related to a nearby <canvas>.

1.54ms

mode attribute changed on various things (no dirty mark), loading attribute on an <image>

0.74ms

<box> is marked not hidden (think this is the autocomplete widget), insert frames into box, some attributes on <richlistitem> and <menuitem> are changed

33.78ms (also 265.18ms reading xbl)

Eight dirty marks caused by scrollbar attribute changes

2.70ms

Observations:

  • There are lots of changes to scrollbars here, despite no scrollbars on mobile Firefox being visible. The set listed in the last row is likely caused by the resize caused by the on-screen keyboard appearing. 120ms of time occurs from when this begins to when this ends, although it is likely that only a small amount of this is directly scrollbar related.
  • I didn’t measure XBL parsing time on desktop Firefox in this test, but it typically takes about 10% of the startup time. Of course, XBL caching should reduce the time to load XBL here. It should be noted though that parsing and creating the main document takes 25% of the rest of the time.
  • The time between reflow 2 and reflow 18 is 550ms. I’m not familiar enough with the mobile code, but this appears to be the time to process and manipulate things after the main document is parsed, equivalent to desktop Firefox’s handling in browser.js.
  • The last two rows are for the autocomplete and on-screen keyboard appearing. It takes up 675ms of time, but the main window has already appeared, so the user’s perception of the time to start up may not need to include this time.

October 4, 2011

XBL Performance Tips

Filed under: Mozilla — enndeakin @ 5:13 pm

As I’ve been investigating XBL caching, I’ve been looking into some additional performance characteristics. Here are some tips on improving your use of XBL in Mozilla, relying on specific implementation details to gain performance benefits. You probably won’t notice much difference, but lots of minor changes can add up.

Properties and methods, that is the code in <property> and method <body> elements are compiled once per session. The code is compiled once when the first element that uses the binding is added into a document. For elements included in a XUL document, this means that they will be compiled while the window is being created and opened. Constructors and destructors are treated similarly.

Fields and handlers (<field> and <handler> elements) however are not compiled when the element is added. Instead, they are compiled on demand when they are used. This means that both fields and handlers do not directly affect the time to open a window, but can instead, in the case of handlers, affect the time to respond to an event.

Fields

The field element defines a value on a element. The code for it is compiled when the field is first accessed, not when it is defined. For example, assume a field exists such as that below:

 <field name="someValue">10 * 50</field>

The code ’10 * 50′ will be compiled and evaluated when code that checks ‘element.someValue’ is encountered. Naturally, if this happens during the binding constructor, this will affect creation time. Although I haven’t investigated this, if this is the case, it might be better to just initialize the field directly in the constructor to save the overhead of having to evaluate the field separately.

However, for fields that are rarely accessed, initialization within the field itself is generally more optimal.

A very important note however, is that the field value is compiled and evaluated not once per session or even once per window. The code in a field body is compiled and evaluated once for every element that uses it. Sometimes, people write large blocks of initialization code directly in the field element. If this field isn’t accessed often, or the binding is only used once or twice in a window, this won’t matter too much. But if the binding is used frequently, this can have notable impact on performance. Instead, call a method from the field initializer such that at least the compilation is only performed once.

It also goes without saying that enumerating an object with a binding will invoke all of the fields and evaluate all of their code.

Handlers

Event handlers defined in XBL are compiled the first time the event occurs. So, for example, a handler for a keydown event will get compiled when the element is focused and a key is pressed down. This saves time when creating the element for rarely used event handlers.

Unlike fields, handlers are only compiled once per window, even when multiple elements exist within the same window. This means that putting all of your code in a method and having handlers that simply wrap the method requires more time to create as methods are compiled when created. If improved creation or startup time is more important to you, then place the code in the handler instead.

It helps to use the event filtering attributes on the <handler> element when possible. These will filter out events that aren’t what the handler code is interested in, and avoids the need to compile and call the script code at all, as these filters are implemented entirely in native code. Attributes for filtering available include: modifiers, button, clickcount, keycode, charcode, and phase.

The description above mainly applies to compile time of the various XBL parts. In all cases, for fields, properties, methods and handlers, the compiled code must still be attached to the content each time the binding is used.

In summary, the choice of how to structure the specific parts of the XBL depends on where better performance is needed.

The display and extends attributes

This isn’t a entirely a performance tip but sometimes the display attribute (or the extends attribute) on a binding is used when it doesn’t need to be, for example:

 <binding extends="xul:box"> 

This particular example doesn’t generally do anything for XUL elements as they are already boxes. The example above might do something if you had changed the display type in a some other manner, but that is a very rare occurrence. The colon-form syntax used here does not mean that it extends some other binding or element. In fact, where it does have a use doesn’t relate to XBL at all. Instead, it specifies which internal layout objects get created for the element.

Although in the past any element could be used for the display/extends colon-syntax, today there are only sixteen tags that are allowed, only half of which are actually useful. (The list of tags to allow was generated by searching the Mozilla source for usage rather than fixing up the cases where it was used and wasn’t necessary).

One such tag that might be worth investigating for performance is using display=”xul:spacer” on a binding. It creates a layout object that will never have children. If you know your binding will have no children, either regular or anonymous, it saves a very tiny amount of extra time and storage (60 bytes or so per element).

Many specific additional tips could be made but it is hard to measure the exact benefit in many cases. I’ve investigated some cases with xbl inheritance, but haven’t found any specific tips there yet. I’m continuing to look into other areas.

July 17, 2011

Status Report for July 17

Filed under: Mozilla, Status Updates — enndeakin @ 7:48 pm

This week I mainly did two things:

  • Made a few minor changes to the patch to handle using popups as drag feedback, to simplify the code.
  • Posted patches to store and read XBL bindings to the startup cache. This is a work in progress, but I spent this week getting the patches to the point where they don’t cause any test failures. There is still some work to be done: various warnings occur when reading the bindings, error handling needs to be improved, there may be a minor memory leak to investigate and, for whatever reason, the cache is only read the second time rather than the first time it is available. The latter is some remnant of the switch to using a startup cache, which stores the cached data in a zip file, rather than the specialized format fastload file.

July 8, 2011

Status Update for July 8

Filed under: Mozilla, Status Updates — enndeakin @ 8:14 pm

This week I mostly finished off a patch to handle panels as drag feedback images. Currently, when dragging, a feedback image is created from the element being dragged, or a script may change the feedback image with the setDragImage function. However, the image is always a static image. With this patch, if you pass a <panel> element to the setDragImage function, the panel will be used directly as the drag feedback, allowing the feedback image to be changed while dragging.

This will allow you to not only change the content and appearance of the drag feedback image while the drag is occurring, but also the position and size. This should allow for some interesting drag effects.

The only other step besides using the setDragImage function, is to ensure that you set type=”drag” on the <panel> element. This ensures that the popup behaves as a drag feedback image. Otherwise, drag events will get fired on the panel itself, and you will likely want them to be ignored and instead fired on what you are dragging over underneath the panel.

See bug 533460 for more details. Some builds are also available with these feature.

Some other things I have done recently:

  • Put up a some updated patches for clipboard event handling.
  • Fixed some elements such as the datepicker and tree such that they cancel mouse scroll events and key events properly
  • An issue where dropping on plugins was not allowed.
  • Some cleanup of xul headers.
  • Changed xml-datasource template generation so that the ids from the source document are used. This allows persistence to work properly.

April 4, 2011

Loading XBL Performance

Filed under: Mozilla — enndeakin @ 3:56 pm

A while ago I spent a bit of time looking at the performance of opening a new window recently. My first few experiments were actually a bit off track as I realized that I had my XUL cache disabled. This isn’t representative of most users, so isn’t completely valid for testing real performance, but it did get me thinking a bit.

In case you’re wondering, the XUL cache does two things. When a XUL file is loaded, it is parsed into an in-memory form and the scripts compiled into a bytecode-like form. When that file is needed again, the already parsed and compiled form is used instead of reading it again. The second step is that the parsed form is saved out to disk in a file. When the browser is restarted, this file is read instead. This way, the source form is not reparsed each time. This process happens for both XUL content and for Javascript. Instead of compiling the script into bytecode each time, the bytecode is serialized to disk and reused the next time. This process improves performance significantly at the cost of a couple of megabytes of disk space.

The file the parsed data is stored in, generally called the fastload file, is currently located in the same place as the network cache and can be found with the filename XUL.mfasl. You can safely delete it and it will get recreated when you start the browser again if you want to see how it affects startup time. (although that test will of course be affected by the time it takes to write it out again).

With the cache disabled, none of the above happens and the files must be reread from the source every time. However, when I had the cache disabled, I noticed that a significant amount of time was taken up by compiling the script associated with XBL bindings. Much less time was used with the cache enabled again.

Now, one thing of note here, is that, unlike XUL documents, only one part of the cache mechanism is used for XBL. XBL is only cached in memory and it isn’t saved out to the fastload file on disk. This means that this compilation time occurs upon each startup. I decided to investigate what would happen if XBL was also saved into the fastload file.

But first, let’s look at the performance with the current behaviour. Reading XBL generally has three steps, the first is to load and parse the source XML document and convert it into an internal representation. The second step is to attach the binding into the document and create the anonymous content. The third and final step is to compile the property and method scripts associated with the binding. Testing shows that the second step takes only 15 percent of the total time, so I’m going to focus on the first and last steps here. (Here I’m only considering the time spent executing within the area of code used to implement XBL.)

This chart shows the time to read a few selected bindings used by the Firefox UI. The first three are some of the pieces that make up the tabbrowser, the fourth is the tree element, the fifth is the dropdown autocomplete popup for the URL address field, and the last is the URL address field itself. This chart only shows a selection of the more complex bindings; in reality about 60 bindings or so are read at startup to create a window.

The chart breaks down the time to load and parse the binding (the blue bar) and the time to compile the properties and method scripts associated with the bindings (the green bar).

The first binding ‘tabbrowser-tabs’ takes a lot longer to parse than the following two tabbrowser related bindings. This should be expected since all three bindings are stored in the same XML file. As the document is only loaded and the XML parsed once per file, we expect that most of the parsing time will be for whatever binding is asked for first. The parse time for ‘tabbrowser’ and ‘tabbrowser-tabbox’ is mostly just overhead from having to locate the cached bindings previously read. (Remember that in-memory caching of XBL is performed currently.)

The compile time for the bindings, especially ‘tabbrowser’ correlates to the amount of script used by that binding. As evidenced, the ‘tabbrowser’ binding has a lot of methods, so significant time is spent on this.

The tree binding shows that a more complex binding that requires both parsing and compiling does indeed require notable time for both parsing and for script compiling.

As with the tabbrowser, the two urlbar bindings are contained within the same file, so the parsing time of the first takes the brunt of the total time. But notice that the ‘urlbar’ also requires significant time to parse as well. Again, there is an explanation. This last binding inherits from the autocomplete binding, so the time here also includes the time to load and parse the base autocomplete binding as well.

As evidenced, script compilation is a significant part of reading a binding. It is this part that we hope to reduce by fastloading.

I implemented a simple XBL fastloading mechanism to see what would happen. The hope is that we can see faster loading if the parsing and compiling steps are replaced by a single mechanism to read data from the fastload file that is already in a format that is close to the in-memory representation used. We can’t eliminate the time entirely of course, as we still need to read the compiled form, but if the original testing is correct, we should be able to eliminate the time needed to compile the scripts at least. The following chart shows the results.

This chart includes a bar showing the time taken when fastloading the same set of bindings (the orange bar) with the original data for comparison.

We can clearly see that the compilation time is entirely gone. The ‘tabbrowser’ binding shows this most obviously as it eliminates over 17 milliseconds off of the original time. But all of the other bindings have also saved this compilation time as well.

In all, it appears that the parse time is reduced by around 20 to 25 percent. In the two cases where parsing is not done, ‘tabbrowser’ and ‘tabbrowser-tabbox’, there is no difference in parse time. Note that in the implementation I did, all of the loading for all bindings in a file happens when the first binding for that file is loaded, so the parse time for all three actually occurs during the first tabbrowser-tabs binding. The small amount that exists is from the overhead of retrieving a binding from a file in the cache that is already been read. That might be worth investigating as well, since this overhead occurs over 500 times just when starting up Firefox.

This last chart shows the total time taken up by XBL parsing and compilation to load all of the bindings using two tests. The first is the time taken during startup. The second is a test which starts the browser, opens the bookmarks window, the sidebar, a couple of panes in the preferences window, a new blank window and then closes them again. As not all elements and bindings are used in the basic Firefox window, this latter test ensures we read a good selection of the additional bindings.

This chart shows that overall, using XBL fastloading removes the time needed for compilation, but has only a marginal effect on the total parsing time.

Note that this testing is only based on a few basic observations, but other tests I’ve done show that similar results occur at least on Windows and Mac, both with optimized and debug builds. Testing suggests about a 3-5% improvement in startup time once the fastload data is cached.

March 2, 2011

Fun with Toolbars

Filed under: Mozilla — enndeakin @ 3:58 pm

I started to work on a prototype for being able to dock panels onto a window, which might be used for the console panel. This would allow one to drag a panel around and snap it into a window so that it was no longer a separate piece of UI.

While looking for a good means to test what I’d implemented, I realized that the simplest was to allow the toolbar to be pulled out of the window.

Or, when dragged near the bottom of the window, the toolbar snaps onto the bottom:

February 5, 2011

Status Update for February 5

Filed under: Mozilla, Status Updates — enndeakin @ 4:36 pm

Things I did this week:

  • Worked on more arrow panel and popup positioning bugs: 524545, 628238, and 629002.
  • A couple of issues with the printer dialog not receiving focus properly: 628157 and 626403.
  • Fixed a problem where pressing the mouse button down to select text but releasing the mouse button on a different frame didn’t cancel selecting mode.
  • Created a patch for an issue where some commands were not working from the context menu for a page loaded in the sidebar.
  • Fixed a problem where key events were being redirected when the focus changes between a content document and a chrome document during the keydown event. An earlier bug had fixed the opposite case.
  • Spent a lot of time on the leak from the resizer patch. It turns out it might not be that patch after all but a global storage related change.

January 21, 2011

Status Update for January 21

Filed under: Mozilla, Status Updates — enndeakin @ 11:11 pm

Some highlights of things I have been working on over the last few weeks:

  • A number of bugs related to focus and mouse behaviour with the tab modal dialogs: Bug 617872, bug 620145, bug 622663 and bug 615186.
  • Arrow panel related issues: Bug 616502 and Bug 616607.
  • Testing why there was a performance issue on Linux with resizers in bug 489303. In addition, bug 626997 where the resizer was allowing the window to be resized even when the window was maximized or in full screen mode.
  • A new property of menus, openedWithKey, was added which returns true if the menu was opened with the keyboard instead of the mouse. This always returns false on Mac. This was implemented in bug 607224.
  • In bug 618907, an workaround was added to resolve an issue where canceling a mousedown event doesn’t move the focus to the window frame that was clicked. A better fix may be implemented in the future.
  • The patch to show resizers on Windows when the statusbar is hidden is causing a leak. See Bug 626956 if you want to help fix it.

November 26, 2010

Status Update for November 26

Filed under: Mozilla, Status Updates — enndeakin @ 6:28 pm
  • Bug 613748 – fixed some issues with keys not working in other tabs when one tab has a prompt displayed.
  • Bug 561243 – clicking the mouse wasn’t focusing or adjusting the caret in the name field in the bookmarks popup. The issue was that the outline on the textbox was trapping the mouse event.
  • Bug 614152 – after opening a menu from a notification, the page’s context menu was not working.
  • Bug 527749 – IE fires the dragleave event at the element being dragged out of after firing the dragenter event on the element being dragged into. The patch in this bug changes to match this behaviour, but as this makes the events fire backwards from what would seem logical and isn’t quite as useful, the older dragexit event is also available which remains unchanged; it still gets fired before the dragenter event as before.

November 12, 2010

Status Update for November 12

Filed under: Mozilla, Status Updates — enndeakin @ 3:22 pm

Bugs worked on this week:

  • Bug 606343 – almost finished with arrow panel alignment as the current patch in the bug seems to work on all platforms.
  • Bug 610378 – fixed an issue where drops were not being allowed when the effectAllowed property was being set.
  • Bug 607224 – added support to handle pressing F10 or the Alt key by itself to this bug which adds a flag to indicate if a menu is being used via the keyboard.
  • Bug 611313 – created a patch which might fix some issues that cause various template tests to occasionally fail.
  • Bug 511010 – worked on some display flickering when closing a menu from the bookmarks panel. I created a patch to not adjust window activation when closing a menu when clicking on a panel.
Older Posts »

Theme: Shocking Blue Green. Blog at WordPress.com.

Follow

Get every new post delivered to your Inbox.