Peformance in the IE WebBrowser control
Peformance in the IE WebBrowser control
steven_pack
Posts: 3Questions: 0Answers: 0
Hi All,
Has anyone managed to improve performance of datatables in the IE Web Browser control (hosted in a .NET WinForms app)? My basic use case is to update 288 cells of a grid without about 70 rows. Peformance is fine in the browser, around:
Chrome: 15ms
Firefox: 45ms
IE9: 87ms
Activex X IE9 Control: 180ms
From what I can gather, the control has known perf issues when traversing the DOM, something about adding all possible attributes to every element, regardless of whether they're used.
Looking at the profiler results in all the browsers, the methods taking the time are
[quote]
fnUpdate (288 calls)
_fnGetCellData (3128 calls)
_fnGetTdNodes (288 calls)
_fnGetRowData (288 calls)
[/quote]
Those all pretty reasonable methods to be taking up the time, and as I said, in the browser they perform fine, but in the web control they struggle. Any tips on optimizing them for the web control, or if anyone has already done so would be much appreciated.
My code for updating is:
[code]
var el = $scope.findGridElement(productId, grid);
if (el) {
var pos = grid.fnGetPosition(el);
grid.fnUpdate(cellValue, pos, column, false, false);
}
[/code]
where el is the and is retrieved using document.getElementById.
Steve
Has anyone managed to improve performance of datatables in the IE Web Browser control (hosted in a .NET WinForms app)? My basic use case is to update 288 cells of a grid without about 70 rows. Peformance is fine in the browser, around:
Chrome: 15ms
Firefox: 45ms
IE9: 87ms
Activex X IE9 Control: 180ms
From what I can gather, the control has known perf issues when traversing the DOM, something about adding all possible attributes to every element, regardless of whether they're used.
Looking at the profiler results in all the browsers, the methods taking the time are
[quote]
fnUpdate (288 calls)
_fnGetCellData (3128 calls)
_fnGetTdNodes (288 calls)
_fnGetRowData (288 calls)
[/quote]
Those all pretty reasonable methods to be taking up the time, and as I said, in the browser they perform fine, but in the web control they struggle. Any tips on optimizing them for the web control, or if anyone has already done so would be much appreciated.
My code for updating is:
[code]
var el = $scope.findGridElement(productId, grid);
if (el) {
var pos = grid.fnGetPosition(el);
grid.fnUpdate(cellValue, pos, column, false, false);
}
[/code]
where el is the and is retrieved using document.getElementById.
Steve
This discussion has been closed.
Replies
1. fnUpdate is O(n)^2 in its speed -
For every extra column, every column is iterated over again. This is because for every cell update results in a call to _fnBuildSearchRow(
oSettings,
_fnGetRowData( oSettings, iRow, 'filter', _fnGetColumns( oSettings, 'bSearchable' ) )
_fnGetRowData internally calls _fnGetTdNodes, which iterates all the td nodes of the row. That call is to maintain the search index. So adding a check:
if (oSettings.aoColumns[iColumn].bSearchable)
before updating the search index, removes the ^2 characteristic and vastly improves the performance.
2. There is no way to bulk update a row.
Yes, you can pass an array or an object to fnUpdate, but internally it recurses itself, so all logic runs for every cell, including calls to _fnGetTdNodes. DOM elements should only be iterated over the minimum number of times. I've added the following method to my copy of datatables:
this.fnUpdateRow = function(aColIndexes, aColValues, mRow, bAction, bRedraw) {}
Internally, this method will call _fnGetTdNodes only once for the entire row update. This combined with the search optimisation above significantly reduces the time taken to update the entire grid. In terms of my particular use case:
Chrome: 5ms
Firefox: 19ms
IE9: 23ms
Activex X IE9 Control: 90ms
Allan, let me know if you'd like to see a patch or anything. I think this sort of bulk update approach and the non-searchable column optimisation gets the grid a lot closer to being suitable for use in finance applications with fast ticking data.
Regards,
Steve Pack
Thanks for your investigation and feedback. With 1.10 I'm going to be introducing an entirely new API into DataTables, and providing a shim layer to map the old API to the new to retain backwards compatibility. As such, a patch of fnUpdate probably isn't of that much use in all honest, since the whole function will be rewritten for the new API - although it is possible others might be interested if you can easily post a diff?
Speed and flexibility for updates, adding and deleting rows are going to make up a good part of the focus in the new API, and I'll very much keep in mind what you mention here.
Regards,
Allan
1. Introduces fnUpdateRow, accepting an aColIndexes and aColValues, which only iterates over the affected td nodes once. (This required some refactoring to reduce copy/paste)
2. Introduces a check for bSearchable before updating the search index
3. Uses innerText instead of innerHTML for updates that don't contain html.
Item 2. was a big perf improvement across all the browsers.
1. & 3. were particularly crucial for getting the IE ActiveX control update times down. As well as being slow to iterate DOM nodes, apparently it blindly recalculates the entire page every time you change innerHTML for any element, as described here: http://msdn.microsoft.com/en-us/library/ms533019(v=vs.85).aspx
----
Index: lib/jquery.dataTables/js/jquery.dataTables.js
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- lib/jquery.dataTables/js/jquery.dataTables.js (revision 6085)
+++ lib/jquery.dataTables/js/jquery.dataTables.js (revision 7209)
@@ -6116,8 +6116,8 @@
_fnSortAttachListener( _fnSettingsFromNode( this[DataTable.ext.iApiIndex] ), nNode, iColumn,
fnCallback );
};
-
-
+
+
/**
* Update a table cell or row - this method will accept either a single value to
* update the cell with, an array of values with one element for each column or
@@ -6142,14 +6142,14 @@
{
var oSettings = _fnSettingsFromNode( this[DataTable.ext.iApiIndex] );
var i, iLen, sDisplay;
- var iRow = (typeof mRow === 'object') ?
+ var iRow = (typeof mRow === 'object') ?
_fnNodeToDataIndex(oSettings, mRow) : mRow;
-
+
if ( $.isArray(mData) && iColumn === undefined )
{
/* Array update - update the whole row */
oSettings.aoData[iRow]._aData = mData.slice();
-
+
/* Flag to the function that we are recursing */
for ( i=0 ; i
your changes sound very impressive. I'm using datatables in a similar manner by hosting the IE Active X control on a Visual FoxPro form. It currently works very well for what I need, but, if your changes makes it perform even better, then I'm all for it! Unfortunately, I don't have a clue how to implement your changes.
Would it be possible for you to share your complete patched code? I would love to see if and how this impacts the performance in my application. Thanks.
I got your code and browsed through it. Thanks for sending it BTW.
From what I saw and understood (which is not much), it does not seem to be something I would be taking advantage of. I thought that your changes would be an immediate benefit "out of the box" and not something that I would need to call on a "as needed" basis. Perhaps I'm just not understanding your improvements (most likely).
On another note. Since you're using the web browser control, have you been able to use Tabletools successfully to print PDF and Excel files? I think that there is something fishy going on with the ActiveX control. I can successfully create the PDF or Excel file if I open a local html file in Internet Explorer. However, when I attempt to navigate to that same local file with the web browser control, it throws errors ("ClearText" is what I think it complained about.) Have you experienced this?