Mozilla faster than Internet Explorer

D

dhplank

Hello everyone,

I've developed a calendar program in javascript, and up until now I've
done most of my testing using Mozilla and Firefox. Everything works
fine, but when I try to use Internet Explorer my response time is
sometimes 50 times slower than using Mozilla.

I know I haven't given you much to go by, but I'm not looking for an
answer so much as an approach to debugging the problem. For example,
does anyone here know of a good web site which deals with
browser-specific javascript performance issues?

Thanks in advance for any helpful hints.
 
R

RobG

dhplank said:
Hello everyone,

I've developed a calendar program in javascript, and up until now I've
done most of my testing using Mozilla and Firefox. Everything works
fine, but when I try to use Internet Explorer my response time is
sometimes 50 times slower than using Mozilla.

I know I haven't given you much to go by, but I'm not looking for an
answer so much as an approach to debugging the problem. For example,
does anyone here know of a good web site which deals with
browser-specific javascript performance issues?

Performance differences between IE and Firefox can sometimes be found
in unexpected places. One strategy is to isolate loops and test
them, then split them in half, then half again, etc. until the
really slow bits are found.

Replacing loops like:

for (var i=0; i<someThing.length; i++)
// mess with someThing
}

with:

var i=0, x=someThing;
do {
// mess with x
} while ( ( x = someThing[++i] ) )

can cause dramatic reductions in processing time, particularly for
large values of someThing.length.

IE tends to be slower than Firefox at DOM operations, but can be
much faster with some others (particularly string stuff in places).

Once you've found the slow bits, post them here and you'll likely get
something better, or just post some of your loops that you think may
be the cause.
 
M

mike

<snip>
Replacing loops like:

for (var i=0; i<someThing.length; i++)
// mess with someThing
}

with:

var i=0, x=someThing;
do {
// mess with x
} while ( ( x = someThing[++i] ) )

<snip>

Is this a "Noticeable" difference?
 
R

RobB

dhplank said:
Hello everyone,

I've developed a calendar program in javascript, and up until now I've
done most of my testing using Mozilla and Firefox. Everything works
fine, but when I try to use Internet Explorer my response time is
sometimes 50 times slower than using Mozilla.

I know I haven't given you much to go by, but I'm not looking for an
answer so much as an approach to debugging the problem. For example,
does anyone here know of a good web site which deals with
browser-specific javascript performance issues?

Not particularly browser-specific, but useful:

http://www.peachpit.com/articles/article.asp?p=31567
 
R

RobG

Duncan said:
mike wrote:

<snip>
Replacing loops like:

for (var i=0; i<someThing.length; i++)
// mess with someThing
}

with:

var i=0, x=someThing;
do {
// mess with x
} while ( ( x = someThing[++i] ) )

<snip>

Is this a "Noticeable" difference?



Only if '// mess with someThing' repeats the expression many times, e.g.
inside an inner loop.

Simply replacing the for loop with:

for (var i=0; i<someThing.length; i++) {
var x = someThing;
// mess with x
}

will give the same speed improvements without the corresponding drop in
readability.


No it wont. Getting someThing.length numerous times is slower than
getting it once, so a similar 'for' loop is:

for (var i=0, j=someThing.length; i<j; i++) {

Using x to store the reference to someThing is faster than getting
the reference every time. Initialising x with 'var' every time is not
required but makes no difference to the speed as far as I can tell:

var x;
for (var i=0, j=someThing.length; i<j; i++) {
x = someThing;
...

Pre-decrementing i is also faster in most browsers (I believe Opera is
slower in some circumstances), so a while loop can look like:

var i=someThing.length;
var x;
while ( i ) {
x = someThing[--i];
// do stuff
}

or

var i=someThing.length;
var x;
while ( i-- ) {
x = someThing;
// do stuff
}

But it iterates through the elements of someThing backwards (which
often is not a problem, but may be if order is important).

Anyhow, there are a thousand variations on the theme, have a read here:

<URL:http://www.websiteoptimization.com/speed/10/10-2.html>

There is a bug in the page, so download it and fix the bug, then run it
(there's a form and a function called 'testLoop').

Also try here:

There is one other way that the do..while loop gives a massive speed boost
though:

if any of the array elements have non-true values the while loop will exit
when it hits the first one whereas the for loop will continue through the
whole array. Obviously aborting the loop after a single iteration instead
of 10,000 would give a noticeable difference. :^)

Not necessarily, they will only exit early if the value of the element
is (part of) the test, e.g.:

while(someThing[++i]) or while(( x=someThing[--i] ))

or similar. But there are plenty of ways to avoid that, e.g. the first
two while loops proposed above will iterate over every element.
 
D

dhplank

Thanks to everyone for the many helpful hints. After reading throught
the hints and performing various tests, I've been able to establish
that the biggest part of the performance problem has to do with how
Mozilla and Internet Explorer handle DOM (Document Object Module)
obects.

For example, the following bit of code:

<CODE>
for (i=1; i <= 366; i++) {

if ((document.saisir["cal_yr1_type_jour_" + i].value != 35) ||
(document.saisir["cal_yr1_active_jour_" + i].value == 0)) {

document.saisir["cal_yr1_active_jour_" + i].value = 0;
document.saisir["cal_yr1_type_jour_" + i].value = 0;
document.saisir["cal_yr1_debut_jour_" + i].value = 0;
document.saisir["cal_yr1_fin_jour_" + i].value = 0;
document.saisir["cal_yr1_theor_jour_" + i].value = 0;
}
if ((document.saisir["cal_yr2_type_jour_" + i].value != 35) ||
(document.saisir["cal_yr2_active_jour_" + i].value == 0)) {
document.saisir["cal_yr2_active_jour_" + i].value = 0;
document.saisir["cal_yr2_type_jour_" + i].value = 0;
document.saisir["cal_yr2_debut_jour_" + i].value = 0;
document.saisir["cal_yr2_fin_jour_" + i].value = 0;
document.saisir["cal_yr2_theor_jour_" + i].value = 0;
}
}
</CODE>

takes 3.8 seconds under Mozilla, but 36 seconds under Internet
Explorer. And in other cases I've found that Mozilla is as much as 100
times faster than Internet Explorer.

In some cases I've been able to better performance by assigning a DOM
object to a variable which I then re-use. However in a case like the
above this doesn't seem possible (since each object is only used once).

Anyone have any ideas?
 
G

Grant Wagner

dhplank said:
Thanks to everyone for the many helpful hints. After reading throught
the hints and performing various tests, I've been able to establish
that the biggest part of the performance problem has to do with how
Mozilla and Internet Explorer handle DOM (Document Object Module)
obects.

For example, the following bit of code:

<CODE>
for (i=1; i <= 366; i++) {

if ((document.saisir["cal_yr1_type_jour_" + i].value != 35) ||
(document.saisir["cal_yr1_active_jour_" + i].value == 0)) {

document.saisir["cal_yr1_active_jour_" + i].value = 0;
document.saisir["cal_yr1_type_jour_" + i].value = 0;
document.saisir["cal_yr1_debut_jour_" + i].value = 0;
document.saisir["cal_yr1_fin_jour_" + i].value = 0;
document.saisir["cal_yr1_theor_jour_" + i].value = 0;
}
if ((document.saisir["cal_yr2_type_jour_" + i].value != 35) ||
(document.saisir["cal_yr2_active_jour_" + i].value == 0)) {
document.saisir["cal_yr2_active_jour_" + i].value = 0;
document.saisir["cal_yr2_type_jour_" + i].value = 0;
document.saisir["cal_yr2_debut_jour_" + i].value = 0;
document.saisir["cal_yr2_fin_jour_" + i].value = 0;
document.saisir["cal_yr2_theor_jour_" + i].value = 0;
}
}
</CODE>

takes 3.8 seconds under Mozilla, but 36 seconds under Internet
Explorer. And in other cases I've found that Mozilla is as much as
100
times faster than Internet Explorer.

In some cases I've been able to better performance by assigning a DOM
object to a variable which I then re-use. However in a case like the
above this doesn't seem possible (since each object is only used
once).

Anyone have any ideas?

Yes, take the suggestions that have already been offered and remove all
those accesses to document.forms['saisir'].elements[your_elements],
cache a reference to the elements collection and use it directly. Also
reverse the direction of your loop (not that it makes much difference,
but when dealing with UI, every millisecond counts):

var f = document;
if (f && (f = f.forms) && (f = f['saisir']) && (f = f.elements)) {
for (var i = 367; i-- > 0;) {
if (f["cal_yr1_type_jour_" + i].value != 35 || /* etc */) {
f["cal_yr1_active_jour_" + i].value = 0;
// etc
}
}
}
 
D

dhplank

Yes, take the suggestions that have already been offered and remove all
those accesses to document.forms['saisir'].elements[your_elements],
cache a reference to the elements collection and use it directly. <<

By taking into account all the suggestions offered, I've managed to
dramatically improve performance. Most transactions now take less than
one second, which is acceptable in the context. Caching the DOM
references is what really speeded things up. I found though that I
have to cache each reference, not just a reference to the form, in
order to get significant improvements.

This means though that I still have one bit of code, which is used to
initialize the page, which is rather slow - it takes about 36 seconds
under Internet Explorer (and only 3 seconds under Mozilla). This bit
of code will only be executed one time per session, so perhaps I can
live with it, but it would be nice to speed it up if at all possible.
Here it is:

<code>

BeginBench();

// Cache DOM values for reuse.
for (i=366; i >= 1; i--) {
c1ac = document.saisir["cal_yr1_active_jour_" + i];
c1ty = document.saisir["cal_yr1_type_jour_" + i];
c1de = document.saisir["cal_yr1_debut_jour_" + i];
c1fi = document.saisir["cal_yr1_fin_jour_" + i];
c1th = document.saisir["cal_yr1_theor_jour_" + i];

c2ac = document.saisir["cal_yr2_active_jour_" + i];
c2ty = document.saisir["cal_yr2_type_jour_" + i];
c2de = document.saisir["cal_yr2_debut_jour_" + i];
c2fi = document.saisir["cal_yr2_fin_jour_" + i];
c2th = document.saisir["cal_yr2_theor_jour_" + i];
}

EndBench("InitRules3");

</code>

(By the way, the DoBench and EndBench subroutines are just used to time
the code.)

I need to work with the hidden "input" fields in the HTML form, as the
whole form is later sent to a parser which recovers everything and
inserts it into a database. I don't think it's feasible to change the
design approach - too much other code is involve.

So ... any ideas on how to reduce the initialization time of this loop,
or am I just stuck with it (get or take a few milliseconds)? Would
getting rid of the loop significantly speed things up? (I might try
that to see.)
 
D

Dr John Stockton

JRS: In article <[email protected]>,
dated Thu, 9 Jun 2005 02:51:10, seen in
// Cache DOM values for reuse.
for (i=366; i >= 1; i--) {
c1ac = document.saisir["cal_yr1_active_jour_" + i];
c1ty = document.saisir["cal_yr1_type_jour_" + i];
c1de = document.saisir["cal_yr1_debut_jour_" + i];
c1fi = document.saisir["cal_yr1_fin_jour_" + i];
c1th = document.saisir["cal_yr1_theor_jour_" + i];

c2ac = document.saisir["cal_yr2_active_jour_" + i];
c2ty = document.saisir["cal_yr2_type_jour_" + i];
c2de = document.saisir["cal_yr2_debut_jour_" + i];
c2fi = document.saisir["cal_yr2_fin_jour_" + i];
c2th = document.saisir["cal_yr2_theor_jour_" + i];
}

So ... any ideas on how to reduce the initialization time of this loop,
or am I just stuck with it (get or take a few milliseconds)? Would
getting rid of the loop significantly speed things up? (I might try
that to see.)



Your document.saisir must be an [array] Object; that code must look
up saisir in document (and there's no doubt a lot there) 3660
times. Try
It = document.saisir
....
c1ac = It["cal_yr1_active_jour_" + i];
which should help.

AIUI, i=366 ; do { ... } while (--i) could be a *little* faster.


You create elements in 10 arrays; it **could** be better, or worse, to
use one array, loaded with a ten-item object - later instead of
clth you'd write A.clth .


You're doing calendar work, AIUI. BE SURE, if you are using Date
Objects repeatedly, to do your calendar in UTC rather than in local
time; if your systems are anything like mine, UTC is several times
faster IIRC. Read my site. Also, date objects may not be particularly
fast in comparison with efficient coding (e.g. for month-length); read
my site.
 
T

Thomas 'PointedEars' Lahn

dhplank wrote:

Please provide proper attribution.
vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
those accesses to document.forms['saisir'].elements[your_elements],
cache a reference to the elements collection and use it directly. <<

Please learn how to quote in Usenet. `>>'...`<<' is not a widely accepted
quotation style, for one part that it confuses parsers designed to
recognize levels of quotation by one more leading character (usually `>')
and thus allow user agents to format the levels differently.

[...] Caching the DOM references is what really speeded things up.
I found though that I have to cache each reference, not just a reference
to the form, in order to get significant improvements.

This means though that I still have one bit of code, which is used to
initialize the page, which is rather slow - it takes about 36 seconds
under Internet Explorer (and only 3 seconds under Mozilla). [...]

<code>

BeginBench();

// Cache DOM values for reuse.
for (i=366; i >= 1; i--) {
c1ac = document.saisir["cal_yr1_active_jour_" + i];
c1ty = document.saisir["cal_yr1_type_jour_" + i];
c1de = document.saisir["cal_yr1_debut_jour_" + i];
c1fi = document.saisir["cal_yr1_fin_jour_" + i];
c1th = document.saisir["cal_yr1_theor_jour_" + i];

c2ac = document.saisir["cal_yr2_active_jour_" + i];
c2ty = document.saisir["cal_yr2_type_jour_" + i];
c2de = document.saisir["cal_yr2_debut_jour_" + i];
c2fi = document.saisir["cal_yr2_fin_jour_" + i];
c2th = document.saisir["cal_yr2_theor_jour_" + i];
}


IMHO this is just the wrong way of "caching". You are simply "caching"
the DOM tree's "leaf" object's references into objects of built-in type,
however the lookups remain (if not increased) as well as the memory usage
does. It take it that you are trying to access a HTML form. So a
reasonable change of the original code would be to assign a (both standards
compliant and downwards compatible) reference to the HTMLFormElement object
(or its `elements' collection as you can see below) to a variable and use
that variable instead. And I sincerely doubt that you need all references
to be "cached" in an array.

`document.saisir' which should be `document.forms["saisir"] is accessed
very often and you are only accessing the form's elements. So:

// Remove redundancies and use proper references
var es = document.forms["saisir"].elements;
for (i=1; i <= 366; i++)
{
if (es["cal_yr1_type_jour_" + i].value) != 35
|| es["cal_yr1_active_jour_" + i] == 0)
{
es["cal_yr1_active_jour_" + i].value = 0;
es["cal_yr1_type_jour_" + i].value = 0;
es["cal_yr1_debut_jour_" + i].value = 0;
es["cal_yr1_fin_jour_" + i].value = 0;
es["cal_yr1_theor_jour_" + i].value = 0;
}

if (es["cal_yr2_type_jour_" + i].value != 35
|| es["cal_yr2_active_jour_" + i].value == 0)
{
es["cal_yr2_active_jour_" + i].value = 0;
es["cal_yr2_type_jour_" + i].value = 0;
es["cal_yr2_debut_jour_" + i].value = 0;
es["cal_yr2_fin_jour_" + i].value = 0;
es["cal_yr2_theor_jour_" + i].value = 0;
}
}

// Introduce proper scoping and optimize loop
for (var es = document.forms["saisir"].elements, i = 367;
--i;) // 0 evaluates to `false' in a boolean expression
{
if (es["cal_yr1_type_jour_" + i].value) != 35
|| !es["cal_yr1_active_jour_" + i].value) // !0==true, !!0==false
{
es["cal_yr1_active_jour_" + i].value =
es["cal_yr1_type_jour_" + i].value =
es["cal_yr1_debut_jour_" + i].value =
es["cal_yr1_fin_jour_" + i].value =
es["cal_yr1_theor_jour_" + i].value = 0; // x=0; y=0; <=> x = y= 0;
}

if (es["cal_yr2_type_jour_" + i].value != 35
|| !es["cal_yr2_active_jour_" + i].value)
{
es["cal_yr2_active_jour_" + i].value =
es["cal_yr2_type_jour_" + i].value =
es["cal_yr2_debut_jour_" + i].value =
es["cal_yr2_fin_jour_" + i].value =
es["cal_yr2_theor_jour_" + i].value = 0;
}
}

Optional optimization steps are

for (var es = document.forms["saisir"].elements, i = 367;
--i;) // 0 evaluates to `false' in a boolean expression
{
for (var j = 3; --j;)
{
var
cal = "cal_yr" + j,
cal_type_jour = cal + "_type_jour_" + i,
cal_active_jour = cal + "_active_jour_" + i;

if (es[cal_type_jour].value) != 35
|| !es[cal_active_jour].value) // !0==true, !!0==false
{
es[cal_active_jour].value =
es[cal_type_jour].value =
es[cal + "_debut_jour_" + i].value =
es[cal + "_fin_jour_" + i].value =
es[cal + "_theor_jour_" + i].value = 0; // x=0; y=0; <=> x = y= 0;
}
}
}

and so on.

The above code is probably triggered by an event. If yes, the even listener
should include a reference to the object that caused the event. E.g. if
the above code is executed in a method named `setZero', it could be changed
as follows:

<script type="text/javascript">
function setZero(o)
{
if ((o = o.form))
{
var e = o.elements;
// ...
}
}
}
</script>
...
<form ...>
...
<script type="text/javascript">
document.write('<input type="button" onclick="setZero(this);">');
</script>
...
</form>

Thus the form element's name does not matter anymore and two more lookups
per each loop are saved.

Further optimization can be done by having the elements have the same name.
Thus in the DOM an elements collection (an HTMLCollection object in a
standards compliant implementation) is created which removes the need for
concatenating the `i' value and for the hard-coded maximum index as it then
can be obtained reading the `length' property of that collection (where
indexes would be 0-based then). This would probably require a minor
redesign of the server-side application, though. For server-side PHP,
names ending with `[]' should be used as they become accessible as arrays
instead of plain values server-side then.

I am not sure where the `35' comes from, however this should not be
hardcoded (at least not repeatedly) as well. This change would decrease
performance a little bit (since it introduces a scope chain resolution
process), but would reduce future maintenance costs.

(And if `0' is the default value and there are no other elements (probably
the hidden `input' elements are not required), the entire script can
probably be replaced with said:
I need to work with the hidden "input" fields in the HTML form, as the
whole form is later sent to a parser which recovers everything and
inserts it into a database.

It is not the parser, really.
I don't think it's feasible to change the design approach - too much
other code is involve.

Depending on what you mean with `design approach', you may or may not
have to accept that this will keep client-side processing slower than
it could be.

I wonder why you find it necessary to set the values to `0'; why do
you not use `<input type="hidden" ... value="0">'?

Furthermore, it appears that you do not take into account that there
are UAs out there where client-side scripting is unavailable and so
you have to provide a viable alternative.
So ... any ideas on how to reduce the initialization time of this loop,
or am I just stuck with it (get or take a few milliseconds)? Would
getting rid of the loop significantly speed things up? (I might try
that to see.)

Yes, it would of course, but it would possibly also remove a feature.
You are to decide whether that is acceptable or not, depending on the
reply you would give to the comment right above.


HTH

PointedEars
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,537
Members
45,023
Latest member
websitedesig25

Latest Threads

Top