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.
Evaluating Local Variables
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.
Cache Variables Whenever Possible
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.
Cache Function Pointers at all costs
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.
function GeneralFunctionPointerMagic()
{
var myElement = document.getElementById(‘myElement’);
// This creates our function pointer and involves a look-up + creation
var funcAppendChild = myElement.appendChild;
// Getting it a second time is faster, only does a look-up
var funcAppendChild2 = myElement.appendChild;
// Calling this is just like any other function pointer
// Note this is a direct invoke with only a local variable look-up
// to find funcAppendChild, with no IE DOM look-ups
funcAppendChild(childElement);
}
function IterateWorkOverCollection()
{
var funcAppendChild = document.getElementById(‘target’).appendChild;
var length = myCollection.getItemCount();
for(var index = 0; index<length; index++)
{
funcAppendChild(myCollection[index]);
}
}
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.
Avoid Using the ‘with’ Keyword
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.
That’s all for Part 1.
Thanks,
Peter Gurevich
Program Manager
Justin Rogers
Software Development Engineer
Comments
Anonymous
January 01, 2003
The comment has been removedAnonymous
January 01, 2003
The comment has been removedAnonymous
August 28, 2006
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).
When will you fix the simple things like substr(-2)?Anonymous
August 28, 2006
I find Tino's comments hilarious, because he totally missed the point of the article.Anonymous
August 28, 2006
For those asking about IE7 performance improvements there is the first of a set of blog posts covering...Anonymous
August 28, 2006
would be nice to see support for Javascript 1.7Anonymous
August 28, 2006
The comment has been removedAnonymous
August 28, 2006
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.
I'm also interested in how you are solving the memory leaks. Do you know about http://novemberborn.net/javascript/edgvl ?Anonymous
August 28, 2006
yes, yes, yes! more plz! ;)Anonymous
August 28, 2006
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).Anonymous
August 28, 2006
Justin -
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?
Thanks again!! I owe you guys some major beers :-)
Cheers,
- BillAnonymous
August 28, 2006
Ooops - forgot to thank Peter for an excellent article. Looking forward to the next one :-)!
Cheers,
- BillAnonymous
August 28, 2006
The comment has been removedAnonymous
August 28, 2006
Good article, thanks!Anonymous
August 28, 2006
Try the following:
var f = document.body.insertBefore;
alert(typeof f); // throws exception, should return "function"
alert(f instanceof Function); // throws exception, should return true
f.apply(document.body, [newChild, oldChild]); // throws as well
Yawn... This has been broken since IE4. No wonder IE is the Netscape of today.Anonymous
August 28, 2006
PingBack from http://www.trapanator.com/blog/archives/197Anonymous
August 28, 2006
Couldn't these performance problems be fixed by adding small lookup cache?Anonymous
August 28, 2006
Great post. Really appreciate.
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 :)Anonymous
August 28, 2006
function IterateWorkOverCollection()
{
var funcAppendChild = document.getElementById(‘target’).appendChild;
var length = myCollection.getItemCount();
for(var index = 0; index<length; index++)
{
funcAppendChild(myCollection[index]);
}
}
--> looks like a bad idea:
function X(){}
X.prototype = {foo : 1, getFoo : function() { return this.foo }};
var y = new X;
y.getFoo();
>>> 1
var z = y.getFoo;
z();
>>> reference to undefined property this.fooAnonymous
August 28, 2006
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.Anonymous
August 28, 2006
> 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).
http://en.design-noir.de/webdev/JS/addEvent/Anonymous
August 28, 2006
The comment has been removedAnonymous
August 29, 2006
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?Anonymous
August 29, 2006
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.Anonymous
August 29, 2006
> var f = document.body.insertBefore;
> alert(typeof f); // throws exception, should return "function"
Alerts 'object' in IE 6.0.2900.2180.xpsp_sp2_gdr.050301-1519 w/ jscript.dll version 5.6.0.8831
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.
> alert(f instanceof Function); // throws exception, should return true
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.
Anyway, this can be fixed pretty easily:
var f = document.body.insertBefore;
if (typeof f == 'object') {
f = new Function('x', 'y', 'document.body.insertBefore(x,y)');
}
alert(typeof f); // alerts 'function'
alert(f instanceof Function); // alerts 'true'
f.apply(document.body, [newChild, oldChild]); // works fineAnonymous
August 29, 2006
I wonder how many of these "complainers" even know what IE's code looks like.
A large product like this isn't simply a "just add this feature and everyone will be happy".
There is a lot more to it. Firefox/Etc. have an unfair advantage in that they used an already working rendering engine, all they had to do was stick on a user interface that people would like.
IE has been around longer than that and thus inherits its past in new versions.
A rewrite would be lunatic, and they can't discard the product. It is part of everything.Anonymous
August 29, 2006
> Anyway, this can be fixed pretty easily:
> var f = document.body.insertBefore;
> if (typeof f == 'object') {
> f = new Function('x', 'y', 'document.body.insertBefore(x,y)');
>}
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?Anonymous
August 29, 2006
The comment has been removedAnonymous
August 29, 2006
Good information, Peter. Looking forward to the next installment.Anonymous
August 29, 2006
PingBack from http://javis.wordpress.com/2006/08/29/recomendaciones-de-performance-en-javascript/Anonymous
August 29, 2006
The comment has been removedAnonymous
August 29, 2006
The comment has been removedAnonymous
August 29, 2006
"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:
-Rewrite the whole select tag
-Use W3C DOM methods to edit the optionsAnonymous
August 29, 2006
The comment has been removedAnonymous
August 29, 2006
The comment has been removedAnonymous
August 29, 2006
"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.Anonymous
August 29, 2006
The comment has been removedAnonymous
August 29, 2006
PingBack from http://www.last-child.com/javascript-performance-tips-from-the-ie7-folks/Anonymous
August 29, 2006
"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.)Anonymous
August 29, 2006
Bookmarked and waiting for parts 2/3Anonymous
August 29, 2006
> 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
> completely "language-agnostic", and thus its methods aren't specially tooled for
> JavaScript: methods are not separate "function" objects.
Now Gecko's native language isn't JavaScript either, and at the same time (a) DOM does look native in JavaScript and (b) it's open to other languages (Python in Gecko 1.9). Must be magic.Anonymous
August 30, 2006
> Aedrin
> I wonder how many of these "complainers"
> even know what IE's code looks like.
I posted some ridiculous parts of IE source code, but moderators censored them. I can post again if anyone wants.Anonymous
August 30, 2006
The comment has been removedAnonymous
August 30, 2006
@Aedrin
By the way, what is the engine you are talking about - gecko was created from the ground up, so is Opera, so is KHTML.
But I am stopping right there, since I have no intention starting a LONG flamewar.Anonymous
August 30, 2006
The comment has been removedAnonymous
August 30, 2006
I had a big issue due to garbage collection in jscript till now, see the thread on (http://groups.google.com/group/microsoft.public.inetsdk.programming.scripting.jscript/browse_thread/thread/d758f148f03cd2ed/cc6143cd1b0d1e38?lnk=st&q=jscript+slow+performance+ie+firefox&rnum=2#cc6143cd1b0d1e3)
I had created a simple htm page (http://www.geocities.com/arvindsagarwal/test.htm) to test how jscript used to behave when lot of objects are created, and the results for ie 6 where really bad.
Good to see dramatic difference in ie7 rc1, and the page shows much better results. My application works much better now on ie7 rc1.
i had given up hopes of anything happening in this area since till last beta also there was no mention of anything to do with Jscript engine.Anonymous
August 30, 2006
"gecko was created from the ground up"
Yes. Which is why I said that FireFox itself builds upon existing technologies (Gecko), as to where IE is built on its own technology (Trident).Anonymous
August 30, 2006
The comment has been removedAnonymous
August 30, 2006
The comment has been removedAnonymous
August 30, 2006
<<I posted some ridiculous parts of IE source code, but moderators censored them. I can post again if anyone wants. >>
1> There's no reason for anyone to believe that your samples are in the product &/or ever were in IE.
2> If you do decide to post copyrighted source code, save Microsoft's lawyers some trouble and post your home address.Anonymous
August 31, 2006
The comment has been removedAnonymous
August 31, 2006
Uhm, creating a variable called "length" is asking for trouble.
It might not be a reserved word, but it's like poking the devil with a sharp stick and not expecting to get burned.
good to see that JavaScript wasnt forgotten, it would be better if there was improvments on the level of support (DOM features etc.) but capping some of the leaks is also good.Anonymous
August 31, 2006
I concur with all that say the reference to document.all should be abandoned. It severely polutes the document object, it is not xbrowser compatible, and therefore leads to bad design.
Have you ever run into code, where the developer created forms called "images", "body", or "links" etc.? Man that makes nightmares.
Please correctly lead the Web development community by posting valid DOM samples, that are xbrowser compatible, and that avoid any reference to proprietary objects/methods.Anonymous
August 31, 2006
Hello again, this is Peter Gurevich, IE PM for ClearType (among other things, as my blog posts have shown).&nbsp;...Anonymous
August 31, 2006
@Ted
{{
<<I posted some ridiculous parts of IE source code, but moderators censored them. I can post again if anyone wants. >>
1> There's no reason for anyone to believe that your samples are in the product &/or ever were in IE.
2> If you do decide to post copyrighted source code, save Microsoft's lawyers some trouble and post your home address. }}
1) I can't prove that the code is still there, BUT
a) I notice some BAD behavior in IE6
b) I found the source code for that part and it looked BAD
c) IE7 has the same visible behavior, so I assume that this part of code wasn't changed.
2) I don't think that getting my address from my Live ID is big deal for them.
P.S. I am pro IE7. I just REALLY want some issues be finally FIXED.Anonymous
August 31, 2006
Are there any improvements in the VBScript engine for IE7?Anonymous
September 01, 2006
> var elementText = BuildTitle() + BuildBody() + BuildFooter();
> document.getElementById(‘target’).innerHTML = elementText;
I my experience I've found that appending the innerHTML of an element rather than manipulating the DOM leaks memory in IE like a leaky sieve.
The function caching is a new idea to me, thanks for that.Anonymous
September 01, 2006
Erik,
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.
Thanks
-DaveAnonymous
September 01, 2006
The comment has been removedAnonymous
September 01, 2006
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...Anonymous
September 02, 2006
The comment has been removedAnonymous
September 02, 2006
pwned :-)
Now I understand why overall perfomance of IE7 is so bad... with half-minute lags sometimes occuring when switching tabs.Anonymous
September 02, 2006
-
@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.
C) Opera kicks butt! (In JS arena at least)
Thanks for reading this ~Anonymous
September 02, 2006
The comment has been removedAnonymous
September 02, 2006
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.Anonymous
September 02, 2006
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.Anonymous
September 04, 2006
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).Anonymous
September 04, 2006
why not compiling:
"a" + b + "c" + d
as if it was:
["a", b, "c", d].join("")
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)Anonymous
September 04, 2006
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.Anonymous
September 05, 2006
Good article, but the comments wre more enlightening!
I'm looking forward to part 2!Anonymous
September 07, 2006
The comment has been removedAnonymous
September 09, 2006
The comment has been removedAnonymous
September 12, 2006
Quelques articles rcents que tout dveloppeur web devrait lire...Anonymous
September 14, 2006
PingBack from http://www.mapthemaps.com/blog/?p=19Anonymous
September 15, 2006
My name is Kaushik. I am a developer on the JavaScript team at Microsoft.
I’ll be blogging mostly about...Anonymous
September 15, 2006
End of the week points of interest:New York Times are working on a WPF tool to improve readability :Anonymous
September 16, 2006
PingBack from http://scriptteaser.com/learningjavascript/script-optimization/Anonymous
September 16, 2006
PingBack from http://web2.0stores.com/blog/archives/microsoft-javascript-perf-tips/Anonymous
September 18, 2006
PingBack from http://globallist.ru/?p=10Anonymous
September 19, 2006
Here are some websites that I referenced during my Ajax talk as well as other sites that provide useful...Anonymous
September 19, 2006
Here are some websites that I referenced during my Ajax talk as well as other sites that provide useful...Anonymous
September 21, 2006
PingBack from http://www.webstandards.org/2006/09/21/ie7-javascript-improvements/Anonymous
September 29, 2006
PingBack from http://www.aesjkt.com/wp/?p=1801Anonymous
September 29, 2006
PingBack from http://www.aesjkt.com/wp/?p=1808Anonymous
October 01, 2006
PingBack from http://www.zhen.org/blog/?p=202Anonymous
October 06, 2006
PingBack from http://www.irishdeveloper.com/ie7/javascript-performance-tips-from-the-ie7-folks/Anonymous
October 06, 2006
PingBack from http://www.jimzimmerman.com/DasBlog/2006/08/29/How+To+Make+Your+Javascript+Run+Faster+In+IE.aspxAnonymous
October 10, 2006
PingBack from http://fatagnus.com/while-we-wait-for-firefox-2-and-ie7Anonymous
October 29, 2006
PingBack from http://blog.flymok.net/2006-09-10/167Anonymous
November 16, 2006
Hello again, this is Peter Gurevich, Performance PM for IE. We have gotten a lot of good feedback fromAnonymous
November 17, 2006
While reading several posts about JavaScript performance [ 1 ] [ 2 ] I did a simple test, too. My firstAnonymous
November 17, 2006
PingBack from http://fri13th.com/blog/archives/209Anonymous
November 21, 2006
PingBack from http://dmtreemenu.wordpress.com/2006/11/18/references/Anonymous
November 28, 2006
PingBack from http://therealcrisp.xs4all.nl/blog/2006/11/28/beware-of-premature-optimization/Anonymous
December 17, 2006
Lorsque j'avais examiné les fichiers JavaScript de Microsoft Ajax Extensions beta 1 une question m'étaitAnonymous
January 03, 2007
The comment has been removedAnonymous
January 04, 2007
Hello again, this is Peter Gurevich, Performance PM for IE. We have gotten a lot of good feedback fromAnonymous
January 10, 2007
PingBack from http://www.anasayfa.org/wp/?p=2560Anonymous
January 16, 2007
I've flagged a few links to noteworthy JavaScript posts over the last month. Yahoo! Video: Advanced...Anonymous
January 16, 2007
I've flagged a few links to noteworthy JavaScript posts over the last month. Yahoo! Video: Advanced JavaScriptAnonymous
January 24, 2007
This is the first ever blog written by me in my entire life time. Let me take some time to introduceAnonymous
January 25, 2007
PingBack from http://learningtheworld.eu/2007/performance/Anonymous
February 08, 2007
PingBack from http://lpetr.org/blog/archives/javascript-smorgasboardAnonymous
February 11, 2007
Peter Gurevich, Performance Programm Manager des IE7 Teams, hat seine dreiteilige Serie zu o.g. ThemaAnonymous
February 12, 2007
IE + JavaScript Performance Recommendations von Peter Gurevich, Programm Manager IE7 IE + JavaScriptAnonymous
March 07, 2007
PingBack from http://www.blogs.ravasthi.name/tepumpkin/2007/03/07/talking-shop-ie-and-javascript-performance/Anonymous
March 15, 2007
PingBack from http://nathanswart.com/2007/03/15/links-for-2007-03-16/Anonymous
March 20, 2007
PingBack from http://rayfd.wordpress.com/2006/11/02/grokking-javascript-variables/Anonymous
September 16, 2007
PingBack from http://mygony.com/archives/1184Anonymous
September 26, 2007
PingBack from http://fineartz.info/ieblog-ie-javascript-performance-recommendations-part-1/Anonymous
October 11, 2007
PingBack from http://efficienttips.com/improve-javascript-performance/Anonymous
December 16, 2007
JavaScript est un langage " late binded " c'est à dire que chaque appel d'une propriété aura un coupAnonymous
December 16, 2007
JavaScript est un langage " late binded " c'est à dire que chaque appel d'une propriété aura un coûtAnonymous
January 16, 2008
Last night, Ron presented to the Memphis .NET Users Group .  The talk began with the improved administrativeAnonymous
January 16, 2008
Last night, Ron presented to the Memphis .NET Users Group .  The talk began with the improved administrativeAnonymous
January 16, 2008
PingBack from http://msdnrss.thecoderblogs.com/2008/01/17/profiling-javascript-with-ajax-view-tool-spot-poor-performance-client-script-in-no-time/Anonymous
May 18, 2008
The comment has been removedAnonymous
October 10, 2008
PingBack from http://www.jedi.be/blog/2008/10/10/is-your-jquery-or-javascript-getting-slow-or-bad-performance/Anonymous
October 10, 2008
PingBack from http://www.jedi.be/blog/2008/10/10/is-your-jquery-or-javascript-getting-slow-or-bad-performance/Anonymous
November 21, 2008
Recently someone asked me about the best practices for AJAX performance. Though this information is scatteredAnonymous
November 21, 2008
PingBack from http://khaidoan.wikidot.com/javascript-articlesAnonymous
January 21, 2009
PingBack from http://qingbo.org/archives/577.htmlAnonymous
January 22, 2009
PingBack from http://www.hilpers.pl/232781-ms-radzi-jak-pisac-wydajnyAnonymous
March 05, 2009
PingBack from http://reallyaced.wordpress.com/2009/03/06/javascript-profiling-in-internet-explorer/Anonymous
March 30, 2009
PingBack from http://www.codingstyle.fr/2009/03/26/quelques-optimisations-en-js/Anonymous
May 26, 2009
PingBack from http://webdevelopment.mobiforumz.com/2008/01/02/how-to-improve-the-front-end-performance-of-a-website/Anonymous
May 29, 2009
PingBack from http://paidsurveyshub.info/story.php?title=ieblog-ie-javascript-performance-recommendations-part-1Anonymous
June 02, 2009
PingBack from http://woodtvstand.info/story.php?id=84076Anonymous
June 02, 2009
PingBack from http://portablegreenhousesite.info/story.php?id=31938Anonymous
June 07, 2009
PingBack from http://weakbladder.info/story.php?id=6263Anonymous
June 07, 2009
PingBack from http://greenteafatburner.info/story.php?id=1217Anonymous
June 13, 2009
PingBack from http://outdoordecoration.info/story.php?id=3282Anonymous
June 16, 2009
PingBack from http://topalternativedating.info/story.php?id=7296