View Full Version : HTC performance

Jul 13th, 2005, 01:58 PM
I posted this in reply to this blog post (http://blog.codingforums.com/index.php/main/blogentry/reducing_code_forking_in_event_handlers/). I'm hoping to get some discussion on this issue here:

I've thought about using HTCs to bring IE up to spec, but I've heard that they have couple performance issues.

1) IE erroneously re-requests the .htc for every bound element. There is a server-side workaround involving setting the expiration header of the .htc, but no client-side solution.
http://slingfive.com/pages/code/xDOM/ (see notes section)

2) It seems like the .htc - or the script inside the .htc - has to be parsed every time an element is bound. IE creates an additional JScript engine for the .htc instead of using the main one. I don't know if it creates the JScript engine for every bound element instead of sharing it for all bound elements, but if it did, that would be even worse.

Articles talking about the performance of csshover.htc (http://www.xs4all.nl/~peterned/csshover.html):
(and more)

(note: I'm not sure if its the HTC aspect of it being slow or the script itself, since the HTC is applied only to the body element)

3) They can cause a memory leak until the page is refreshed or directed to another page. Specifically, IE doesn't deallocate memory when a behavior is dynamically removed, which means it can happen when the bound element is removed. Thus, pages that are dynamically changed a lot will start hogging up more and more memory.

From http://msdn.microsoft.com/workshop/author/behaviors/howto/using.asp:
Note A behavior attached to an element through CSS, using a style rule defined in the document, is automatically detached from the element as soon as the element is removed from the document tree. Once the element is removed, all associations of that element to any element in the document tree, such as style sheet rules that specify behaviors, are stripped from the element as well. On the other hand, a behavior attached to an element through an inline style, or through the addBehavior method, is not automatically detached from the element when the element is removed from the document hierarchy. These two methods of applying behaviors have no dependencies on the containing document, and therefore the behavior remains applied to the element even as it is removed from the document tree. The removeBehavior method can be invoked to explicitly detach this type of behavior from the element.

The only "fix" is outside the control of the developer - the user has to edit his/her registry.

Then I tried this Element prototyping HTC (http://blog.codingforums.com/index.php/main/blogentry/element_prototyping_in_internet_explorer_55_and_above/) on a page with ~500 elements in the body. I'm using Tomcat on top of Apache with the default config, plus

# set IE .htc expiration to 1 day
ExpiresActive On
AddType text/x-component .htc
ExpiresByType text/x-component A86400

to fix issue 1. On this 1.6Ghz Pentium M laptop, the page took ~1.5 seconds to load (and reload) and during that time IE was unresponsive.

Another test: I saved this page (newthread.php) as an htm, which has around 650 elements in the body, added the behavior code, and loaded (and reloaded) it up in IE: took ~3 seconds to load.

Then I tested a much lighter HTC: DOM2 event support (http://blog.codingforums.com/index.php/main/blogentry/reducing_code_forking_in_event_handlers/) on the same page (newthread.php). With the HTC: ~600ms. Without the HTC: ~250ms. That's more reasonable, but still bad.

During all this testing, I noticed another large problem: unloading the page takes much much longer and the browser is also unresponsive around this time. For newthread.php and the DOM2 support HTC, it took around 1 sec to unload. Unfortunately, this lag occurs after the window unload event, so I couldn't accurately time it.

Now I test it on a copy of the JavaScript forum listing (forumdisplay.php), which has around 1100 elements in the body. With the DOM2 support HTC: ~800ms to load, ~3 secs to unload. Without the HTC: ~420ms to load, nearly instantaneous unload.

So is there any way to improve the performance of HTCs, especially when applied to many elements?

Jul 13th, 2005, 02:26 PM
Probably not without a lot of careful engineering (for lack of a better term). Isolating a problem during construction is most always less involved than diagnosing a set of interdependencies. (I don't really know what I'm trying to say, except take it one step at a time... if someone else's code is the problem, then start over). :eek:

Jul 13th, 2005, 03:01 PM
So...what does that have to do with this? If I can't get HTCs to execute fast enough, I'll ditch it and use conventional functions.

Jul 13th, 2005, 03:17 PM
It has to do with proper usage. Either they suck all together, or someone is coding them improperly, so if diagnosing the real problem requires an unacceptable amount of work, then why bother?

Jul 13th, 2005, 04:27 PM
As the author of those HTC's, I must explain a little about what is going on. The prototyping HTC is *very* expensive, and not because it is an HTC. To fake the inheritance, I actually dump the entire prototype each time an element is bound. Say we have n properties/methods in the prototype, and m elements to bind. Then we have O(n*m) performance hit. It's more an academic exercise than a useful one.

The DOM2 Event HTC, however, simply adds 2 methods to each element. It takes O(1) to assign a new method, and so we only have an O(m) performance hit. It's also declared not to have ViewLink content, so I assume IE makes optimizations to it.

Whatever overhead caused by the HTC's is made insignificant by the massive inefficiency of the psuedo-prototyping method. Not that it can be done much better. A possible optimization is taking a static "screenshot" of the prototype. If you're not extending at runtime, then after your initial prototypes are set, you can removeBehavior from each of the elements in the page, and the prototype assignment should still remain.

Jul 13th, 2005, 04:53 PM
My comments weren't meant to be a criticizm of anyone's code, by the way, just a generalization... ;)

Jul 13th, 2005, 06:50 PM
Thanks for replying jkd :) (lol enumerator)

I wrote up a quick equivalent of the DOM2 event HTC to compare performance:

var EventTarget = {};

EventTarget.addEventListener = function(type, listener, bCapture) {
this.attachEvent('on' + type, listener);

EventTarget.removeEventListener = function(type, listener, bCapture) {
this.detachEvent('on' + type, listener);

window.attachEvent('onload', function() {
var base = document.body;
var nodes = base.getElementsByTagName('*');
for (var i = 0; i < nodes.length; i++) {
var node = nodes[i];
for (var prop in EventTarget)
node[prop] = EventTarget[prop];
for (var prop in EventTarget)
base[prop] = EventTarget[prop];

The disadvantages is that the script is delayed until the window.onload and that new elements won't support the new methods. The latter point can be solved by replacing document.createElement and each element's cloneNode with functions that would fix the new element(s) up, but that would I suspect that would make the script at least twice as slow, and the new createElement and cloneNode methods would be slightly slower than the original ones.

In any case, here are the results for a copy of the JavaScript forum listing (forumdisplay.php):

With above script: ~570 ms load, near instantaneous unload

With HTC: ~760 ms load, 3-4 sec unload

Without script/HTC: ~440 ms load, near instantaneous unload

So the HTC is significantly slower than the script (though if the script included the modified createElement and cloneNode, this might not be the case) when loading the document. However, the biggest problem for HTCs is apparently the unload performance.

Jul 14th, 2005, 12:24 PM
BTW, I also noticed that the lightWeight attribute has a huge impact on performance. When lightWeight isn't "true", it took ~2.6 secs to load, up from ~760 ms.