IE + JavaScript Performance Recommendations – Part 1

Hello again, this is Peter Gurevich, Performance PM (among other things) for IE7. We have heard a lot of requests to improve our Jscript engine, especially now that AJAX sites are becoming more prevalent on the web. I want you all to know that we have been listening and have recently made some great fixes to our engine to improve the garbage collection routine and to reduce unbounded memory growth. You should see noticeable improvements on AJAX sites in the Release Candidate we shipped last week. I want you also to know that performance of the object model and JavaScript engine will be an area that we focus on strongly in future releases.

While investigating the performance issues on script heavy sites we noticed several design patterns that resulted in less than optimal script performance. Changing these design patterns on the site end often resulted in huge performance wins (4x to 10x increase) to the user, so I wanted to share these recommendations with everyone.

To that end, this blog will be the first in a 3 part series focusing on developing performance optimized scripts for web pages, covering the following:

Symbolic Look-up recommendations

JavaScript code inefficiencies

Script and performance considerations specific to Internet Explorer

Please let me know if there are other useful performance topics you’d like to hear about.

Symbolic Look-up Recommendations

A primary source of JavaScript performance issues when running inside of IE come from constant symbolic look-up. Symbolic look-up occurs whenever the JScript engine tries to pair a name or identifier in the script with an actual object, method call, or property running in the context of the engine. Most of the time these objects are IE Document Object Model (DOM) objects and while there are general performance tips for working with any JScript host there are also specific IE considerations that can help when writing DHTML.

Local variables need to be found based on a scope chain that resolves backwards from the most specific scope to the least specific. Sometimes these symbolic look-ups can pass through multiple levels of scope and eventually wind up in generic queries to the IE DOM that can be quite expensive. The worst case scenario is that your variable doesn’t yet exist and every scope in the chain is investigated, only to find that an expando variable needs to be created.

function WorkOnLocalVariable()

{

local_variable = ObtainValueFromDOM();

return (local_variable + 1);

}

Above is a sample of a poorly written function where we have a local_variable that we are attempting to define within the function scope, but without a preceding var declaration will actually be looked up in all scopes. If we don’t find the variable, a new global will be created, otherwise an existing global will be used. This new variable is now accessible to other methods as well and can sometimes cause odd behaviors in your code.

The recommendation here is to precede your variables with var if you are truly defining them in the current scope. This will prevent the look-up and your code will run much faster. You’ll also prevent aliasing against global named objects. A simple typo such as using the variable “status” without declaring it (“var status”) will result in the use of the window.status property within the IE DOM, so be careful.

Every binding in JScript is late. This means each time you access a property, variable, or method a look-up is performed. Within the IE DOM, this could mean an extensive search of the element to find the same property over and over again, only to be returned to the JScript engine unchanged from the previous request. A simple example of overactive symbolic look-up on a DOM property follows.

function BuildUI()

{

var baseElement = document.getElementById(‘target’);

baseElement.innerHTML = ‘’; // Clear out the previous

baseElement.innerHTML += BuildTitle();

baseElement.innerHTML += BuildBody();

baseElement.innerHTML += BuildFooter();

}

You have to imagine here that the functions are constructing HTML for inclusion into our base element. The above code results in many lookups of the property innerHTML and the construction of many temporary variables. For instance, the line where we clear the property does a look-up followed by a property set. This isn’t so bad. The next lines each do a property get for the initial text, followed by a string concatenation and then a property set. Both the property get and set involve name look-ups for the property. Ignore the string concatenation for now a faster version of this code would attempt to circumvent all of the extra name resolution.

function BuildUI()

{

var elementText = BuildTitle() + BuildBody() + BuildFooter();

document.getElementById(‘target’).innerHTML = elementText;

}

We now do a single property set of the target element and internally within the DOM the clearing operation is free (or as free as it can get for this example).

Another form of this same problem is in intermediate result caching. Often times code for web pages is written based on some top level base element where all interactions are going to start from. A perfect example is the document object. If I were going to write a simple calculator function based on values within the DOM it might be written like the following.

function CalculateSum()

{

var lSide = document.body.all.lSide.value;

var rSide = document.body.all.rSide.value;

document.body.all.result.value = lSide + rSide;

}

In any compiled language the above will produce some heavily optimized code. Not so in JScript, since everything is interpreted and delay bound. Up front, JScript doesn’t know if the value property is going to manipulate the DOM and therefore change the values of the intermediate results. It can’t cache the intermediates all on its own. You’ll have to help it along. In the above case we can cache everything up to the all collection and improve our performance by eliminating multiple look-ups of three different variables.

function CalculateSum()

{

var elemCollection = document.body.all; // Cache this

var lSide = elemCollection.lSide.value;

var rSide = elemCollection.rSide.value;

elemCollection.result.value = lSide + rSide;

}

You can also cache functions. JScript and IE both handle this differently, so I’ll cover this in the following section.

Remember that everything is a look-up so calling the same function over and over again, also involves a look-up each time. Depending on if you are using a JScript function or an IE function pointer, these look-up operations will each entail a different amount of work. First, we’ll look at the simplest case of using a JScript function since they are quite a bit lighter than IE’s function pointers.

function IterateWorkOverCollection()

{

var length = myCollection.getItemCount();

for(var index = 0; index<length; index++)

{

Work(myCollection[index]);

}

}

Normally you wouldn’t cast a second glance at that code, but if there are a significant number of elements you are operating on the constant look-ups performed to find the Work function can start to slow you down. Taking the Work function and assigning it to a local variable only takes a few extra milliseconds and can prevent the constant look-up.

function IterateWorkOverCollection()

{

var funcWork = Work;

var length = myCollection.getItemCount();

for(var index = 0; index<length; index++)

{

funcWork(myCollection[index]);

}

}

The speed savings in JScript for this type of operation are minimal, but within IE, when working with DOM functions you can get even more from this process. Internally, whenever you invoke a function off of an object, the script engine will do a name resolution, followed by an invoke of the target method. In addition, there is a local scope name lookup for the object itself. The same work loop using an IE element will definitely be more expensive and involve more look-ups.

function IterateWorkOverCollection()

{

var parentElement = document.getElementById(‘target’);

var length = myCollection.getItemCount();

for(var index = 0; index<length; index++)

{

parentElement.appendChild(myCollection[iterate]);

}

}

We still have the local variable look-up (which is faster than the function look-up from JScript), but then immediately following we have a function name look-up on the element (so we have two look-ups, ouch). Finally we get an invoke call as mentioned. We can speed this up by removing the function resolution entirely and creating a function pointer. A function pointer will encapsulate the name look-up as a DISPID and the object to call that function on and so the invoke process is more streamlined. The initial creation of the function pointer is slightly more expensive, but this creation is only incurred the first time you create a function pointer on a given element for a given method. The following code details the basics of IE function pointers, how they work, and how we can rewrite the work function to be slightly faster.

Caching function pointers isn’t a 100% guaranteed savings since we’ve shown there is overhead in the first creation and the look-ups involved. In cases where you only call the function a couple of times it probably doesn’t make sense to do the extra caching.

The ‘with’ keyword in JScript can be used to define a new scope local to the element you are working with. While this operation makes it easy to work on local properties it also modifies the scope chain making it more expensive to look up variables in other scopes. Further appending to the scope chain can take enough time that a simple usage of the ‘with’ statement to set only a small number of properties can be more expensive than writing more verbose code.

You CalculateSum() example is hilarious, not only because of the use of document.all that has been deprecated since IE5 and should have been removed completely from IE7 because this backwards-compatibilty still causes global namespace pollution and many quirks, but mainly because a value-attribute from the DOM is always a string so you are not actually adding up 2 values, you are concatenating them.

For the rest a lot of this seems like premature optimization to me. Maybe you should mention something about the ultra-slow stringhandling of Microsoft’s JScript and the way to overcome that by using arrays and join(”) (yes, it’s ugly).

Tino’s comments aren’t bad at all. My example was slightly contrived and does require a specialized control in order to work properly. If you wanted it to work with the default value property on a standard DOM element you’d need to do some conversion. The focus of the article was specifically on the things you could do to improve performance, not really ensure that each code sample stood on it’s own right. In fact, we’ve found most people already have plenty of their own samples that they can apply these changes to for immediate performance wins.

Since I’m on the IE team, I can’t really comment on "features" of the JScript language directly. In fact, most of the goodness above is about how JScript works, but is also very much applicable to how the IE DOM is constructed and how we interact with the JScript engine. I’d like to keep any postings we do on the topic of improving the link between the two components and not on the internal specifics of components I don’t work on. If you have problems with IE on the other hand, we may be able to help out. So feel free to ask any JScript+IE integration questions or maybe just about IE performance issues you’ve had.

The problem with these examples is that they lead to unnecessary code obfuscation. As Tino noted, stay away from premature optimalization.

If I remember correctly one thing that slows JScript down is it’s garbage collection: if you have a lot of objects it’ll check them all, everytime, even if they stay in existance for a long time. It would be more efficient to decrease the checking of long-existing objects.

Script optimization is always a good thing (though Tino’s right, use of document.all should be avoided if possible).

I noticed that there isn’t any word about the W3C Event Model, but I assume that’s because in order to implement it, IE’s entire event model would have to be rewritten (seeing as IE starts on the target element and bubbles the event upwards by default).

Please pass along my thanks and ‘excellent work’ on the new version of JScript. We are doing heavy, heavy AJAX and have noticed significant speedups (not quite as fast as Mozilla on ‘large working sets’ but much, much faster than JScript 5.6).

Speaking of which, the new JScript 5.7 .dll seems to work fine when installed in a system using IE6 (and the performance improvements remain). Any chance we’ll see this new .dll as part of a ‘security patch update’ or whatever to IE6?

One of the typical examples for performance critical scripts is a client side sortable list – either based on a table or on DIVs – with more than a couple hundred rows and a dozen columns.

In intranet applications it is a common task to create a list control looking and behaving as similar as possible to the "real" Windows Forms ListView control – without using Java, ActiveX, .NET or something *g*

A post how the fastest script implementation from your point of view could look like would be awesome 🙂

Tino/Mark, the bit you seem to have missed is that scripts don’t get optimized heavily by a compiler (because there isn’t one), so it’s important to know how to write performant code when necessary. Sure if you have a tiny bit of rarely called JS, then write for maintainability. If it’s doing heavy work though, you need to put in some effort to make it work as well as possible.

> I noticed that there isn’t any word about the W3C Event Model, but I assume that’s because in order to implement it, IE’s entire event model would have to be rewritten (seeing as IE starts on the target element and bubbles the event upwards by default).

As for the previous comments, I agree. It’s silly to talk about speed optimization when your implementation sucks. It sounds like "guys, look, we have a really good implementation, you – the web developers – have to work with us on this, fix your sites, make them faster, we’ve done the best we could".

I’ll take this problem to another level: if a guy learning web development sees only bad examples all year-long, he’ll code as bad as in the examples. I already *know* document.all and similar dirt is, *cough*, just dirt. However, beginners, novices, amateurs can’t tell the difference. All of these bad examples go back into the head, you get used to them.

wrong wrong wrong — all the examples you cited could easily be optimized by the JS interpreter. If the scope hasn’t changed there is no reason that the interpreter could not maintained a MRU cache of references per scope. Asking the developer to (a) trade readability for performance and (b) optimize for IE will have unpredictable results on other browsers and make the code harder to refactor later. Why not make the JS engine more intelligent at handling the examples above?

Some time ago I had to optimize some heavily inefficient JavaScripts/DHTML’s. To find out, what exactly was slowing things down, I ran a series of tests on different aspects of IE’s JS. What I found out was, that most everything works very very quickly, until it has to start performing lookups within DOM. For example, one of the slowest things was getElementById (and similar element-finding functions). Try making a page with 6000 checkboxes and then obtaining a reference to each of them. 😉 IE’s lookup times seem to be growing linearly with the increase of the elements. As if it was running through ALL of them instead of a binary search. Other browsers do that better. 😉 A solution to this is to cache the reference once you have it – then everything works quickly.

Another nice thing would be if it would be possible to temporarily "turn off" rendering engine while performing some layout changes. For instance, I had to set up dynamically some >100 little "bars" inside a complex HTML when the page loaded. Each time I changed some property (width, position) of each bar, IE seemed to recalculate the whole page. Eventually I found out that if I positioned them absolutely, IE did it much faster, because they did not affect the rest of the page’s layout. Still – seems like a workaround to me.

IE has always returned ‘object’ for ‘typeof’ on DOM methods. And since methods on DOM objects are not defined by ECMA-262, but instead are DOM implementation specific, they can be ‘typeof’ anything the implementation requires.

However, my point is, alert(typeof document.body.insertBefore) does not "throw an exception" as described above, so the case of typeof x == ‘object’ can be caught and handled without a try/catch block.

Does not throw an exception in IE 6.0.2900.2180.xpsp_sp2_gdr.050301-1519 w/ jscript.dll version 5.6.0.8831, it alerts ‘false’, so again, this case can be caught and handled programmatically.

> f.apply(document.body, [newChild, oldChild]); // throws as well

This does generate an "Object doesn’t support this property or method" error because ‘f’ isn’t a Function, and does not have an apply method. However, because you can test whether ‘f’ has an apply method prior to using it, you can also avoid generating an error here.

> Yawn… This has been broken since IE4.

> No wonder IE is the Netscape of today.

Broken? Where is the specification for what a web browser DOM object’s methods should return for ‘typeof’?

Oh, that’s right, the web browser DOM isn’t part of the ECMA-262 specification, so it can behave however the implementation chooses to have it behave.

Sure, you can wrap every "IE function" (to use Peter/Justin’s nomenclature) in a real ECMA function object. That way the DOM methods and others like XMLHttpRequest will behave normally and have all the features like a prototype and constructor. It already works that way in Firefox and Opera, so you would only need to take the performance hit of wrapping everything for IE. Unfortunately, the methods are attached to each DOM element. I can’t think of an easily reusable way to handle this IE quirkaround, it would have to be sprinkled everywhere you needed a real function from a DOM function. Anyone else have a better idea?

I’ve been doing heavy-duty AJAX applications (tens of thousands of lines of client side JScript code) in IE for over six years now and I have to say that IE JScript performance has steadily improved over those years. The suggestions mentioned here are known to me (except the function caching…thanks!), and I can say they really do make a huge difference particularly where lengthy iterations are concerned.

I’d really like to see Part II cover two additional topics (one of which was discussed above):

1. String concatenations: slow, fast, and ugly; a HOW TO. This should certainly include discussions of array.join("") as suggested above.

2. innerHTML vs. the DOM. I recall reading an MSDN article ages ago that suggested that when building a select tag with lots of options dynamically it was actually faster to replace the entire tag via the innerHTML of its parent than it was to iterate through the select tags options collection adding options one at a time. This remains fantastic advice to this day (the more options in the select tag the better). Any other such tips surrounding innerHTML vs. the DOM?

3. Dare I even ask: how browser specific are your suggestions? In my experience, Safari handles string concatenations much better than IE, and its array.join("") method is much slow making that hack a very IE specific tuning mechanism. Any thoughts?

Grant: You are right that typeof document.appendChild == "object" in IE6 but it is not an instance of Object (sigh). I got the exception in IE7rc1 when writing code in the address bar which is most likely just a UI bug.

It is also true that the W3C DOM does not enforce you to use real objects and methods. But it is encouraged to use the native language constructs where they fit and this is an obvious case.

Let me make an example of how bad this is. Let’s say that the .NET implementation of the W3C DOM API was done in the way it is exposed to JScript. It would prevent people to use reflection (because methods are not methods and properties are not properties and interfaces are not interfaces) and you would not be able to put DOM nodes in a hash table that expects Object (because DOM nodes are not Objects).

"2. innerHTML vs. the DOM. I recall reading an MSDN article ages ago that suggested that when building a select tag with lots of options dynamically it was actually faster to replace the entire tag via the innerHTML of its parent than it was to iterate through the select tags options collection adding options one at a time. This remains fantastic advice to this day (the more options in the select tag the better). Any other such tips surrounding innerHTML vs. the DOM? "

If I recall correctly, the article isn’t regarding it being faster. Its about a bug in IE6 that will clear a select tag of all its options if one attempts to use innerHTML on the select tag to change it’s contents. Due to the bug one must either:

"If I recall correctly, the article isn’t regarding it being faster. Its about a bug in IE6 that will clear a select tag of all its options if one attempts to use innerHTML on the select tag to change it’s contents. Due to the bug one must either:

-Rewrite the whole select tag

-Use W3C DOM methods to edit the options"

Actually, I’ve never done it that way probably because of the bug you mention. I wrap the select tag in a span tag and call innerHTML on the span rather than the select.

> > I noticed that there isn’t any word about the W3C Event Model, but I assume that’s because in order to implement it, IE’s entire event model would have to be rewritten (seeing as IE starts on the target element and bubbles the event upwards by default).

"It is also true that the W3C DOM does not enforce you to use real objects and methods. But it is encouraged to use the native language constructs where they fit and this is an obvious case. … To sum it up. This is ridiculous."

I disagree. First of all, the "native language" for MSHTML is C++, as that is what it is written in. Secondly, MSHTML’s implementation seems to be primarily built around OLE Automation constructs (IDispatch), first created by the VB people long before JavaScript was invented; then, it was expanded to support Active Scripting concepts such as expandos (via IDispatchEx).

MSHTML object methods are just that: methods on the underlying COM object. MSHTML is completely "language-agnostic", and thus its methods aren’t specially tooled for JavaScript: methods are not separate "function" objects. And I don’t think that they should be. Besides the memory and performance implications that would have, it would require a huge re-engineering of 10 years’ worth of code, for little or no benefit.

(That said, I’m not really sure why "typeof document.appendChild" would be object–I would expect it to fail altogether.)

> Secondly, MSHTML’s implementation seems to be primarily built around OLE Automation

> constructs (IDispatch), first created by the VB people long before JavaScript was invented;

Why should this bother a web developer? I really don’t care about Microsoft’s good reasons — if they can’t afford a decent JavaScript and DOM implementation, they shouldn’t publish browsers. At least they shouldn’t encourage "optimizations" that depend on their quirks.

> First of all, the "native language" for MSHTML is C++, as that is what it is written in.

> MSHTML object methods are just that: methods on the underlying COM object. MSHTML is

How Microsoft can promote standards, while using themselves non-standard code?

What’s to the value of the article, while I find the variables part useful, the part of functions caching is totaly missing the point.

Why should I think, whether is is COM or anything else underneath? It is am implimentation details, and since I am not going to perform any heavy algorhitm using JavaScript, I do prefer to just keep my code clean (again, var helps keeping the code clean).

"And anyway, I was under the impression that IE was originally an extension of the Mosaic browser… "

IE 1 and 2 were standalone EXEs whose rendering engine was, for the most part, based on Mosaic, from what I know.

IE 3 was componentized into several DLLs including the rendering engine, MSHTML.DLL. I don’t know how much of IE3’s MSHTML was based on Mosaic. However, I know that by IE4, MSHTML was entirely MS code (Trident), and no longer based on Mosiac at all.

Interestingly, up through IE6, MS still credits Mosaic in the About box. IE7, however, has removed that text.

Excellent article! Can’t wait for part 2. For those of you who are saying this is nothing but premature optimization, I think you have missed the point, which was to show you baseline examples of how to reproduce the cases. Knowing where the engine shines and where it doesn’t surely is good thing.

I do have some questions for the IE+JScript integration team and I think some posters have already commented on it. When is IE going to expose the DOM prototypes? This really hurts developers considering most other browsers support it. I understand that DOM objects are refcounted COM objects and the JScript engine doesn’t truly own them, but isn’t there some way you could emulate them as special cases? There has to be a solution to this other than "IE is special and it’s hard" right? Are all of the components of IE really that decoupled from eachother?

I think the root of this issue, as I understand it, is that JScript is a complete standalone component and could be used outside the context of MSHTML. By the same token, MSHTML is a standalone component, and can be used without JScript. This decoupling prevents the JScript team from adding code to deal specifically with MSHTML COM objects, which makes sense because it shouldn’t have to know about outside components. But, can’t we all agree that the web browser is a special case and certainly warrants whatever work needs to be done to fix it? Is it out of the question to think about branching a special version of MSHTML and a special version of JScript specifically for IE that are intimate enough to make this and many other long-standing technical issues be solved?

"I want you all to know that we have been listening and have recently made some great fixes to our engine to improve the garbage collection routine and to reduce unbounded memory growth."

Are you referring to the cyclic COM object reference memory leaks? This probably ties right in with my other comments about possibly tightening the relationship between MSHTML + JScript specifically for the IE product. Coupling the 2 components would certainly bring about solutions to the memory leaks because maybe JScript can inspect COM refcounts (I don’t know the internals, I am speculating, anyone who wants to clarify please do). What I do know is that, technical difficulties or not, gigantic memory leaks in IE are holding up large scale client-script driven apps.

There has been no real work done on the VBScript engine for IE7. We expect to focus on cross browser technologies in the future so JavaScript is likely to receive more atention that VBScript moving forward.

I tested the above also on my laptop running winXP with IE7RC1 but the results show the same: performance of the ‘tips’ is actually worse than the ‘normal’ method, which means that ‘caching function pointers’ and ‘caching DOM methods’ is useless.

Caching DOM references however is still a good idea, but I guess everone already knew that…

By the way: when I reference a method on an object and execute that method I expect it to be executed in the global scope; the way that MS seems to have implemented the DOM ECMAScript bindings is completely illogical.

I’m also looking forward to part 2 and 3, I hope they will be as interesting as this article 😛

@IE-team: just take a look at benchmark 3 in this comment for a tiny math performance issue. And Love the article by the way, looking forward to later parts.

@Tino and few others:

I Love these quibbling comments of yours Tino! I actually like people with a meticulous sense of fault-finding. Well, there’s a downside to it that when people complain a lot, their strictures precision wears off logarithmically. In this particular case, I might pin Peter for his rather ambiguous exposition on the matter which started it all in the first place 🙂 "No explanation of any kind or amount is enough; Ever!" would have been a good memo.

Anyway, for a follow-up to Tino’s unprofitable experiment, I’ve done some of my own, with some interesting results. There’s a little misunderstanding in Tino’s take on the function caching Peter has stressed on. The point was about functions in places where they’re completely out of scope of each other like those cutesy little objects we create in JScript. Below, I’ve written some over-simplified JS to check them out.

My machine setup: a generic overclocked Intel.

IE6 sp1 on win2k (yep, another old school win2k fan over here!)

FF 1.5.0.6 and Opera 9.01

——————————————————-

/* Benchmark 1 */

function myObject1()

{

this.get = calculate;

function doSomeDivision(num)

{

return num/1.31415;

}

function calculate(num)

{

var i=300000;

while(i–)

{

num = doSomeDivision(num)+doSomeDivision(2.71828);

}

return num;

}

}

function myObject2()

{

this.get = calculate;

function doSomeDivision(num)

{

return num/1.31415;

}

function calculate(num)

{

var doSomeDivision_pointer = doSomeDivision;

var i=300000;

while(i–)

{

num = doSomeDivision_pointer(num)+doSomeDivision_pointer(2.71828);

}

return num;

}

}

function bench1()

{

var s = new Date().getTime();

var obj1 = new myObject1();

var out = obj1.get(1234567890);

var e = new Date().getTime();

alert (e-s);

//————————————–

var s = new Date().getTime();

var obj2 = new myObject2();

var out = obj2.get(1234567890);

var e = new Date().getTime();

alert (e-s);

}

bench1();

———————————————————————————————

Benchmark 1 results:

IE:

(no-cach): 1453

(cached ): 1328

+125ms benefit.

FF:

(no-cach): 2515

(cached ): 2438

+77ms benefit.

Opera:

(no-cach): 406

(cached ): 391

+15ms benefit.

Well, It’s clear (to me at least) that the article may have not belonged in the garbage bin as previously thought after all.

By the way, as I was playing with the setup, I found out that we can optimize the code big time if we reference functions to object they belong to (using "this" property).

———————————————————————-

/* Benchmark 2 */

function myObject3()

{

this.get = calculate;

function doSomeDivision(num)

{

return num/1.31415;

}

this.doSomeDivision = doSomeDivision;

function calculate(num)

{

var i=300000;

while(i–)

{

num = this.doSomeDivision(num) + this.doSomeDivision(2.71828);

}

return num;

}

}

function bench2()

{

var s = new Date().getTime();

var obj3 = new myObject3();

var out = obj3.get(1234567890);

var e = new Date().getTime();

alert (e-s);

}

bench2();

———————————————————————-

Increase in performance is significant especially in FF:

Benchmark 2 results:

IE:

( no-cach ): 1453

(object-ref): 1100

+353ms

FF:

( no-cach ): 2515

(object-ref): 1094

+1421ms!

Opera:

( no-cach ): 406

(object-ref): 406

No difference!

I’ve no idea what gives about how the way IE and FF look-up system interprets these references that it yields this huge boosts but they definitely enjoy this a lot.

=============

Ok, for some non-relevant performance issues, I found IE (and opera) can use some guess work when dealing with cracked mathematicians:

/* Benchmark 3 */

function bench3()

{

var s = new Date().getTime();

var i = 500000;

while(i–)

{

var num= 1234.12345 / 0;

}

var e = new Date().getTime();

alert (e-s);

}

bench3();

——————

IE: 391ms, FF:47ms, Opera: 266ms

Anything divided by zero means infinity. A lot of people know this, including FireFox! Yes, this is incredibly stupid setup, but if IE’s Jscript people do us a favor and take care of this we might save ourselves from hearing a lot of things like "IE is STUPID dude! FF knows anything divided by zero is infinity but IE has to ask the co-processor every single time; dude."

And for the last test I show you it’s not the calculation part that makes the Opera to be the boss here:

/* Benchmark 4 */

function bench4()

{

var s = new Date().getTime();

var i = 500000;

while(i–)

{

var num= Math.sqrt(3.1415);

}

var e = new Date().getTime();

alert (e-s);

}

bench4();

—————————–

IE:265ms, FF:718ms, Opera:312ms

It’s clear Opera’s strength lays in the way its script engine compiles the JS instruction on the air. I’d love to know how they’ve done that.

===================================================

Conclusion:

A) Although people working at Microsoft may have been some Homo Sapiens descended from some random ape-like creatures which were themselves descended from other nasty things, they make things or write articles that are actually credible most of the time, in contrast with people working in open source holy community which are intelligently designed and make sacred things like FireFox and will rule the world sooner or later.

B) FireFox community could definitely use some members with "I Love to Complain" t-shirt on. IE community is heavily using them right now with lovely results.

I didn’t jump to conclusion about cached DOM methods above until I’m totally sure Tino trash talk was right on the money. Now I’m pretty much sure it was. Excellent job Tino!

Caching function pointers is an unavoidable rule of thumb and I think Peter simply extended this principle to cover DOM methods which backfired with the performance reek Tino glorified above. I reckon the reason might be over-optimization, like double-zipping an already zipped file. I believe IE has already optimized its DOM method access and we have got to avoid overdoing it. Anyway, unless there has been something we totally mistook (or something they’re still working on), our Performance PM got some amendment to do about this one.

Interestingly, this whole caching function pointer technique still holds water with custom functions we define on the DOM objects as the article expected:

As I can see, IE definitely needs to reconsider some inner DOM look-up optimizations; With FF we apparently hit the overdoing wall again and Opera rocks as usual.

Keep it up Tino, as I’ve always said, any community could use some flying brickbats – fair or foolish. Whether your technique is pure pessimism or simply a ‘throw enough stuff at the wall’ approach, just keep it up.

AnonL: thanks for the heads-up. My sarcasm is actually more an expression of frustration having to deal with IE’s quirks, bugs and shortcomings on a daily basis and the fact that that wil not change much with the coming of IE7.

Now for your excellent post on the caching of functionpointers: I still think that in 99% of all cases there will be just one step down in the scopechain to look up some global function, and as my benchmarks show this is only more costly. For the other 1% where this technique actually might improve performance you would have to ask yourself if this 125ms over 300.000 lookups (something you will rarely do in normal code) really makes a difference. As you yourself splendidly demonstrated with your second benchmark (although I would prefer prototyping the methods, especially when you plan to create many of those objects) it makes much more sence to refactor the code in such way that you don’t need to do lookups down the scopechain at all, and in production code there are probably far better candidates for optimization than those lookups. In fact, in my experience heavy javascript applications only spent a very small amount of time executing arbitrary javascript code and most of the time repainting and rendering the interface.

AnonL: as for your benchmark no. 5 it must be noted that these 2 methods (cached and non-cached) are not really the same; in the non-cached version myMethod is executed in the scope of the DOM object, in the cached version it is executed in the global scope. I think that would explain the difference in IE where DOM objects are actually COM objects and this interface might just be responsible for the delay.

Isn’t there any way for the Javascript engine to fix the way it handles the + operator so that it will be parsed as a list of items to add, then items will be parsed and computed, and the type of the first computed item in the list will indicate how to compute the final operation in the list (concatenation or numeric addition)?

If so, the + operator would no longer be a binary operator evaluated step by step in a chain, but instead would be avaluated by compiling an expression that first takes the sum of the string lengths, allocates a single buffer for the result, and then performs the concatenation with a single copy into that buffer.

Compare this to how Java compiles a String concatenation using an intermediate StringBuffer (before Java5) or an unsynchronized StringBuilder (in Java5+), which gets transfermed into a String object only at end of the concatenations: it is much smarter than IE’s implementation of string concatenation in JavaScript, and a lot faster because it saves many reallocations. This is especially important when generating complex strings with many items, such as the string to assign to an .innerHTML property of a document or <div> container, with many items concatenated with +.

Of course this won’t resolve the case of the += operator used many times. For this issue, one must use an heuristic like the one used in Java’s StringBuffer or StringBuilder, that allocates space with an heuristic for possible extensions: the extra space may be freed only when converting it to the final non-mutable String, if this really saves enough bytes, or if there’s a system in the Garbage Collector that allows freeing asynchronously the extra unused space at end of non-mutable arrays and buffers (like the "string" object backing store buffers); but this may require changing the objects allocation descriptors (with a way to specify the effectively used length and to mark the mutable status), so may be a lot of work in the GC implementation (this is not needed in Java because it natively differentiates the backing store of arrays from simple fixed-size objects, so the length of arrays and the mutable status has always been part of the internal representation, also because Java uses multiple heaps for arrays and for fixed-size objects, to speed up the GC and the reuse of freed objects in the fixed-size heaps, without needing multiple moves for the same objects when compacting the heap free space).

I really hope that IE’s implementation of the Javascript GC will continue to be improved (because the current memory usage of IE is still too large, and its GC is extremely slow to reclaim the unused memory into a much smaller heap with faster data locality that performs much better with the processor cache and the extra slow virtual memory committed to disk).

except that it would be using an intrinsic method (non-overridable, except if IE provides a way to override operators like in C++, something that is external to ECMAScript design), without parameter, that checks the datatype of the first element of the array to determine if this is concatenation or concatenation instead of a function lookup for join, something like:

["a", b, "c", d]."__plus__"()

This way of compiling would be prefered if there are more than 2 elements in the array (otherwise the array construction will just loose time needlessly)

verdy_p: are you seriously suggesting that Microsoft should change it’s ECMAScript implementation (which is a standard) to something non-standard?

Are you one of those people that think that Javascript should be more like Java? or Python? or Ruby? It’s not, get over it and deal with it.

The ‘problem’ with the + operator being used both as an arithmic operator and a string operator depending on the operands is something that only folks novice to Javascript seem to have problems with. Now I’m not saying that you are a complete n00b when it comes to Javascript but I’m sure you will understand that changing some fundamental behavior in a language that has been widely used for years is something that simply cannot be done, and surely not just by one party – Microsoft still has enough work to do fixing those areas that still aren’t compliant to existing specifications and standards.

I’m late again. In case someone is still following this comment threads…

@Tino:

Just to sort out my conclusion on the DOM function referencing, I might say it is basically a pretty bad idea to start with. About benchmark 5 above, I don’t think the scope change – although been drastic and convincingly an erratic behavior – is the main reason behind browser’s better performance, evidently it’s simply more a cached pointer at work. Concerning real DOM functions, well, they turned out to be no cutesy cuddly objects at all. No browser gives you a function pointer on DOM method inquires. Apparently when we try to do so, they retrieve a pointer to an alien Object that is itself cloned from the DOM interface. That’s where we draw a line between JavaScript playground and browser interior structures. They simply don’t fraternize. I tried to loop through (using "for…in") one of those mutations which crashed mshtml.dll out of IE. FF simply tosses a dead non-native Object. I guess IE’s COM implantation still allow the object to do its job at the expense of performance downgrades and irregularities like that ugly crash.

Just a simple note to myself:

* * * * * * * * * * * * * * * * * * * * * * * * * *

"No funny business with DOM methods".

* * * * * * * * * * * * * * * * * * * * * * * * * *

And about that frustration part, I think all these people commenting here would give you an "I’ve been there" monologue of their own labor. Personally I imagine I’ve relocated my high expectations to IE8 long ago; just to keep my calm. Or sanity.

I’d like to respond to Tino’s comments on some of the optimizations. Yes, we know that in some cases the optimizations aren’t optimal for other browsers. We are trying to provide insight in to making IE faster and each of these samples has not been fully tested in FireFox or Opera. With that in mind, lets take a quick look at the three benchmarks that were performed which contradict the article.

Your first benchmark is spot on. Your scenario was much simpler than most. There was nothing polluting the global namespace and there was only a single level of scope. So caching global function pointers in your sample produced a slower result. When you are looking at much larger sites, or mash-ups, where script is pulled in from many sources then the caching of script engine based function pointers begins to win. If you add in deep scope chains, caching the global function pointer also begins to be faster. We know that the performance gain in this space is minimal. Our main recommendation is to cache DOM function pointers, which I’ll explain next.

Your second benchmark points out DOM function pointers as coming out slower. This is not the case. A DOM method call from the script engine requires a name to ID look-up, followed by an invoke using that ID. When you’ve cached the method we wrap it in a special function object that already knows the ID to invoke on and so it performs a quicker invoke, by about 15%. I believe your scenario may have fallen prey to the operations being performed. At the end of this comment I’ve added a code sample that shows getElementById (caching this function) to be faster by about 15%. Please feel free to run the numbers by yourself. Note, care was taken not to allow the JavaScript GC and other functionality of the system to affect the timing of the calls. The sample where many text nodes were being created and appended falls prey to these systems and provides inaccurate results.

@AnonL

You had some script where you attached a JScript function as an expando on a DOM element. Then you cached this guy off to use it. This is a perfect example of when caching really comes in for a huge win. Glad you pointed it out. It is a shame that all of the combinations of different types of objects being shared between IE and the JScript have different performance. This is a by-product of supporting any IActiveScript language. After all, IE does work perfectly well with PerlScript, VBScript, PythonScript, and many other script engines today and so a lot of code remains to support those engines.

Someone noted that an enumerator on top of a cached DOM function would result in a crash. You’ll be happy to know that this was fixed in IE 7.