SmartGWT Nightly build 2011-02-11
GWT 2.1.1
Browser: Chrome 9.0
OS: Mac OS X 10.6.6, 2.4GHz Intel Core 2 Duo
It takes 24,924ms to select/deselect all records from list grid of 5000 records.
Here is the set up:
The culprit seems to be in ListGrid.GridBody.updateRowSelection.
Based on my basic understanding from looking at SmartClient's code, when selectAllRecords is invoked, it traverses through each record item and invokes setSelected to mark the item as selected. It also marks the selection object as dirty so it can be redrawn later. There is an observer on setSelected that at the end of this call it invokes _rowSelectionChanged, which internally invokes updateRowSelection. Here is the definition for this method:
Every time this method is invoked (i.e. for each item), it calls lg.getSelection to recompute the selected records which is very expensive. I think most of the code in this method is unnecessary for select all/deselect all. Furthermore, if the comment stated above this method is true, it *only* needs to call lg.updateSelectionCanvas() anyway. Here is my proposed change:
With this change, it only takes 254ms to select all/deselect all records: 99% performance improvement.
This problem exacerbates when dealing with tens of thousands of records which is what we are running into in production. We are currently resisting making this change on our end because it involves making code change at implementation level in SmartClient javascript framework. Please make the fix ASAP so everyone can benefit from this.
Thanks,
Kevin
GWT 2.1.1
Browser: Chrome 9.0
OS: Mac OS X 10.6.6, 2.4GHz Intel Core 2 Duo
It takes 24,924ms to select/deselect all records from list grid of 5000 records.
Here is the set up:
Code:
ListGrid grid = new ListGrid(); grid.setWidth100(); grid.setHeight100(); grid.setFields(new ListGridField("name", "Name")); grid.setSelectionType(SelectionStyle.SIMPLE); grid.setSelectionAppearance(SelectionAppearance.CHECKBOX); ListGridRecord[] records = new ListGridRecord[5000]; for (int i = 0 ; i < records.length; ++i) { BsdJs.Map map = BsdJs.Map.create(); map.put("name", "Name " + i); records[i]= new ListGridRecord(map); } grid.setRecords(records); grid.draw();
Based on my basic understanding from looking at SmartClient's code, when selectAllRecords is invoked, it traverses through each record item and invokes setSelected to mark the item as selected. It also marks the selection object as dirty so it can be redrawn later. There is an observer on setSelected that at the end of this call it invokes _rowSelectionChanged, which internally invokes updateRowSelection. Here is the definition for this method:
Code:
// override updateRowSelection to update selectionCanvas if necessary updateRowSelection : function (rowNum) { var lg = this.grid; if (!lg) return; if (lg.showSelectionCanvas) lg.updateSelectionCanvas(); if (!lg._dontRefreshSelection) { this.invokeSuper(isc.GridBody, "updateRowSelection", rowNum); } if (lg.getCurrentCheckboxField() != null) { var cellNum = lg.getCheckboxFieldPosition(); if (lg && !lg._dontRefreshSelection) lg.refreshCell(rowNum, cellNum); var validData = (isc.isAn.Array(lg.data) || (isc.isA.ResultSet(lg.data) && lg.data.allMatchingRowsCached())), selection = lg.getSelection() || []; if (validData) { if (selection.length == lg.data.getLength()) { lg._setCheckboxHeaderState(true); } else { lg._setCheckboxHeaderState(false); } } } else if (lg.getTreeFieldNum && lg.selectionAppearance == "checkbox") { var treeCellNum = lg.getTreeFieldNum(); if (!lg._dontRefreshSelection) { lg.refreshCell(rowNum, treeCellNum); } } },
Code:
// override updateRowSelection to update selectionCanvas if necessary updateRowSelection : function (rowNum) { var lg = this.grid; if (!lg) return; if (lg.showSelectionCanvas) lg.updateSelectionCanvas(); if (lg._dontRefreshSelection) return; this.invokeSuper(isc.GridBody, "updateRowSelection", rowNum); if (lg.getCurrentCheckboxField() != null) { var cellNum = lg.getCheckboxFieldPosition(); lg.refreshCell(rowNum, cellNum); var validData = (isc.isAn.Array(lg.data) || (isc.isA.ResultSet(lg.data) && lg.data.allMatchingRowsCached())), selection = lg.getSelection() || []; if (validData) { if (selection.length == lg.data.getLength()) { lg._setCheckboxHeaderState(true); } else { lg._setCheckboxHeaderState(false); } } } else if (lg.getTreeFieldNum && lg.selectionAppearance == "checkbox") { var treeCellNum = lg.getTreeFieldNum(); lg.refreshCell(rowNum, treeCellNum); } },
This problem exacerbates when dealing with tens of thousands of records which is what we are running into in production. We are currently resisting making this change on our end because it involves making code change at implementation level in SmartClient javascript framework. Please make the fix ASAP so everyone can benefit from this.
Thanks,
Kevin
Comment