AdvancedDataGrid column switching performance
At work, we've been having some significant performance issues with the Flex
AdvancedDataGrid
control, so my boss has
begun a dialog with some of the AdvancedDataGrid
control's developers over at Adobe. As part
of that dialog, my boss asked me to write a brief summary of the problem - which I've done. But it has occurred to me
that the problem description is likely to be very interesting to some people, so I've decided to publish it here too.
And so, here it is...
Here is a brief description of the way we use the AdvancedDataGrid
control, and why I believe it is so slow when used
in this way.
Introduction
We use the AdvancedDataGrid
to display XML data in a hierarchical data tree. The XML / hierarchical data contains
items of various types such as people, and emails. Initially, the AdvancedDataGrid
shows only a single navigation
tree column, the label field of which simply contains a "summary" of the properties for each item. However, when the
user selects a row, we then switch the AdvancedDataGrid
's columns to a set of detailed columns specific to the
selected row's type. For example, if a person is selected, then we might show "Firstname" and "Surname" columns.
We currently achieve this behaviour by performing two key actions on the AdvancedDataGrid
's (selection)
change
event:
column set switching, and column span switching.
Column Set Switching
To switch the AdvancedDataGrid
's column set, we do the most obvious thing: assign a new array of
AdvancedDataGridColumn
objects to the
AdvancedDataGrid.columns
property. This is very slow - more on this later.
Column Span Switching
Initially, the navigation tree (which shows a text summary of each item) spans all columns. However, when a row is
selected, and thus detailed columns set for that row's type, then we do not want the navigation tree to span all columns
for rows of that type, otherwise the tree will completely overlap the detail columns.So, we use a custom
AdvancedDataGridRendererProvider
class that overrides the protected checkMatch
function to dynamically set its own
columnSpan
property according to the assigned data object, and selected row type. This currently works quite well.
The Problem
The problem is that the AdvancedDataGrid
is extremely slow when switching columns. This seems to be a result of the
AdvancedDataGrid
destroying, and then recreating all of it's item renderers any time the column set is changed.
Specifically, the following calls / events occur:
- The
AdvancedDataGrid
'scolumns
setter function sets a privatecolumnsChanged
flag to true. - As a result, the
[
AdvancedDataGrid.commitProperties()
[http://livedocs.adobe.com/flex/3/langref/mx/controls/listClasses/AdvancedListBase.html#commitProperties()) function calls the (super)AdvancedDataGridBaseEx
'scolumns
setter function. - The
AdvancedDataGridBaseEx.columns
setter function calls the privateAdvancedDataGridBaseEx.columnRendererChanged()
function for each column. AdvancedDataGridBaseEx.columnRendererChanged()
sets the protected (super super)AdvancedListBase.rendererChanged
flag totrue
.- This causes the
AdvancedDataGridBaseEx.updateDisplayList()
function, when next called, to call a privateAdvancedDataGridBaseEx..purgeItemRenderers()
function. - Of course,
AdvancedDataGridBaseEx.purgeItemRenderers()
discards all of theAdvancedDataGrid
's item renderers. - Finally, the various
updateDisplayList()
functions in theAdvancedDataGrid
class hierarchy go about recreating all of the item renderers from scratch.
When the application window is small, the above steps can be reasonably quick. However, when the application is maximised, it can easily be showing more than 50 rows, with at least 10 columns - so well over 500 item renderers are being destroyed and then recreated. The Flex Builder Profiler suggests that the construction phase, particularly component measurement, is the most time consuming part.
Showing / Hiding Columns Instead
Since assigning values to the AdvancedDataGird
's columns
property always results in a full recreation of all
renderers, we've considered avoiding that assignment by merely showing / hiding the desired columns instead. While this
is indeed a bit faster, it is still quite slow (a lot of item renderers still need to be re-measured, etc) and then
exposes several other bugs / limitations in the AdvancedDataGrid
.
For example, as mentioned above, we use a custom AdvancedDataGridRendererProvider
class that changes its columnSpan
property according to the assigned data object, and selected row type. This works correctly when the column set is
re-assigned, since the recreation of the item renderers causes the AdvancedDataGrid
to evaluate the
AdvancedDataGridRendererProvider
's
columnSpan
property. However, if the recreation is avoided by showing / hiding columns instead, then there is currently no way for
the AdvancedDataGrid
control to recognise that the navigation column's columnSpan
property has / should change.
This seems to be because the AdvancedDataGrid
only evaluates the AdvancedDataGridRendererProvider
's columnSpan
property when rows are being constructed, and not at any other time.
Possible Adobe Fixes
Be conservative when purging item renderers
As mentioned above, when the AdvancedDataGrid
columns are changed, all item renderers are purged. It seems to me,
that at the very least, it would be better if the AdvancedDataGrid
was able to correctly purge only the
no-longer-needed renderers, and simply resize those that remain (assuming that they are of the same renderer class of
course). Perhaps even better, would be some kind of caching of unnecessary item renderers, to make reinstating columns
quicker too.
Fix / extend the showing / hiding columns code
As mentioned above, there are numerous bugs in the AdvancedDataGrid
that are exposed when columns are shown and
hidden - especially if the item renders are not all the same height, in which case the AdvancedDataGrid
gets quite
confused about how high a given row should be. If these bugs get fixed, and the AdvancedDataGrid
extended to allow
cells to change their columnSpan
property, then this may be a better approach from a performance point of view.
Unfortunately, though, given just how slow Flex seems to be a measuring and
laying out a couple of hundred items, I doubt the AdvancedDataGrid
will ever be truly fast at changing columns when
populated with data. Perhaps there needs to be some far more fundamental work done to Flex's layout routines. Indeed,
even laying out a dozen components can be slow under Flex, so its no wonder that 500+ components is so slow.
By the way, we've tested the AdvancedDataGrid
from the pre-release
version of Flex Builder 3.1.0 (2511), but that has not helped to improve these particular issues in any discernible way.
I'll keep you posted on any feedback we get from Adobe... though I'm not expecting any real solutions any time soon :|