DOM building methods

8 views
Skip to first unread message

keemor

unread,
May 13, 2009, 4:12:11 AM5/13/09
to Prototype & script.aculo.us
Hi,

I'd like to present 3 ways of building ul list and compare the speed
of them.
I used firebug's profiler on FF3

html:
<body>
<a href="#" id="go">go</a>
<ul id="list"></ul>
</body>
$('go').observe('click',go);

First method:
Time to build (2122.406ms, 84055 wywołań)
function go(e){
e.stop();
$('list').update();
var tpl = new Template ('<li class="#{class}" id="#{id}">#{id}</
li>');
$R(1, 1000).each(function(i){
$('list').insert(tpl.evaluate({
class: 'klasa',
id: i
}));
})
}

Second:
Time to build (1754.548ms, 57053 wywołań)
function go(e){
e.stop();
$('list').update();
$R(1, 1000).each(function(i){
var li = new Element('li', {
class: 'klasa',
id: i
}).update(i);
$('list').insert(li);
})
}

Third:
Time to build (513.747ms, 30054 wywołań)
function go(e){
e.stop();
var tpl = new Template ('<li class="#{class}" id="#{id}">#{id}</
li>');
var list = new String;
$R(1, 1000).each(function(i){
list += tpl.evaluate({class: 'klasa', id: i});
})
$('list').update(list);
}

As you can see third method is 4 times faster than first one.
In first two methods around 50% of time took
extractScripts() & stripScripts() which are used in update and insert
methods.

So if you have more complex RIA with many dynamic elements you can
simply improve it significantly :)



Иван Генчев

unread,
May 13, 2009, 6:32:12 AM5/13/09
to prototype-s...@googlegroups.com
Hi, how did you collect timing data? I use FF3.5b4 with it's JIT
JavaScript engine an I would like to see how it will change things for
all three methods.
> ------------------------------------------------------------------------
>
>
> No virus found in this incoming message.
> Checked by AVG - www.avg.com
> Version: 8.5.325 / Virus Database: 270.12.27/2111 - Release Date: 05/12/09 18:03:00
>
>

keemor

unread,
May 13, 2009, 8:19:05 AM5/13/09
to Prototype & script.aculo.us
Use Profile from firebug manually or :

console.profile([title]) & console.profileEnd()

http://getfirebug.com/console.html

RobG

unread,
May 13, 2009, 8:55:56 AM5/13/09
to Prototype & script.aculo.us


On May 13, 6:12 pm, keemor <kee...@gmail.com> wrote:
> Hi,
>
> I'd like to present 3 ways of building ul list and compare the speed
> of them.
> I used firebug's profiler on FF3
>
> html:
> <body>
>         <a href="#" id="go">go</a>
>         <ul id="list"></ul>
> </body>
> $('go').observe('click',go);
>
[...]
> Third:
> Time to build (513.747ms, 30054 wywołań)
> function go(e){
>         e.stop();
>         var tpl = new Template ('<li class="#{class}" id="#{id}">#{id}</
> li>');
>         var list = new String;
>         $R(1, 1000).each(function(i){
>                 list += tpl.evaluate({class: 'klasa', id: i});

That line throws a parse error in Safari 4, class is a reserved word
in ECMAScript, use className.

>         })
>         $('list').update(list);
> }
>
> As you can see third method is 4 times faster than first one.
> In first two methods around 50% of time took
> extractScripts()        & stripScripts()    which are used in update and insert
> methods.
>
> So if you have more complex RIA with many dynamic elements you can
> simply improve it significantly :)

If speed matters, use plain javascript. Marginally more code, but the
result is 3 times faster in Firefox than "Third": (94ms vs 34ms in
Firefox 3 on a 1GHz G4) and twice as fast in Safari 4 (39ms vs 19ms).


--
Rob

T.J. Crowder

unread,
May 13, 2009, 9:49:47 AM5/13/09
to Prototype & script.aculo.us
Hi,

> As you can see third method is 4 times faster than first one.

Yes. The third method largely evaluates to setting the innerHTML
property of the list element, which uses the browser's built-in HTML
processing. Reading HTML strings and quickly turning those into the
displayed result is fundamentally what browsers *do*, and they're
highly optimized to do it as quickly as possible (people are endlessly
comparing browsers' page rendering times). They can do it working
directly on their internal structures, whereas if you create and
append elements via DOM methods, you're working through a third-party
API layered on top of the browser's internals. No surprise, then,
that the browser does it faster when you let it use its native
implementations of things. :-)

I did a similar exercise a while back (here[1]), comparing DOM methods
vs. Prototype's wrappers for DOM methods vs. concatenating a string
and setting innerHTML. The last method, innerHTML, wins on every
browser I tried, hands down, usually by at least one order of
magnitude. (Prototype's wrappers aren't very costly, but going
through the DOM methods is, and so they inherit that cost.) The
smallest discrepancy, IIRC, is on Chrome ("only" about four times
faster) -- but then, Chrome is freakishly fast at nearly
everything. :-)

[1] http://pastie.org/476791
--
T.J. Crowder
tj / crowder software / com
Independent Software Engineer, consulting services available

Romek Szwarc

unread,
May 13, 2009, 11:20:24 AM5/13/09
to prototype-s...@googlegroups.com
Thanks T.J. for this long answer.
Maybe not many people will face such a performance problem, but this could be written in documentation, so we can avoid such issues at the beginning.
This could be also good topic for PimpMyCode series at Prototype's blog.

Greetings
Romek

2009/5/13 T.J. Crowder <t...@crowdersoftware.com>



--
keemor

Josh Powell

unread,
May 13, 2009, 1:53:51 PM5/13/09
to Prototype & script.aculo.us
It's a very common development pattern/problem that comes up.
Creating a bunch of repetitive DOM elements and inserting them into
the page.

It comes down to:

1) Native for loops will always be faster then calling a function
that then does the loop
2) Native inserts are faster then library inserts, but library inserts
are cross browser and may handle other niceties

3) inserting after a loop is faster then inserting in a loop:

var arr = //some long array
for (var a=0, length = arr.length; a < length; a++) {
// create string
}
// insert string

is faster then

var arr = //some long array
for (var a = 0; a < arr.length; a++) {
// create string
// insert string
}

4) Joining arrays is usually faster then concatenating strings, but
not as easy to read. I only try this if I really need the speed.
Sometimes += strings are faster.
var arr = //some long array
var data = [];
for (var a = 0; a < arr.length; a++) {
data.push(a);
}
insert data.join('')

5) DOM node vs appending strings speed is browser dependent. Chrome
and nightly safari are slightly faster creating DOM nodes, appending
them all to a doc fragment and then appending the doc fragment to the
document then at doing innerHTML. They are so fast at doing either,
just code to your preferred method for them. It won't even matter for
really long arrays. Current gen browsers are much faster at inserting
a string.

6) innerHTML = 'string' does not work in IE 6, 7, 8 for inside table
tbody and other tags.

7) watch out for IE memory leaks when using innerHTML or prototypes
remove or similar methods, clear events on your DOM first. This isn't
built in because it is very slow to traverse the DOM and remove every
event automatically and is unnecessary overhead on many occasions.

Cheers,
Josh Powell

RobG

unread,
May 13, 2009, 10:39:34 PM5/13/09
to Prototype & script.aculo.us


On May 13, 11:49 pm, "T.J. Crowder" <t...@crowdersoftware.com> wrote:
> Hi,
>
> > As you can see third method is 4 times faster than first one.
>
> Yes. The third method largely evaluates to setting the innerHTML
> property of the list element, which uses the browser's built-in HTML
> processing. Reading HTML strings and quickly turning those into the
> displayed result is fundamentally what browsers *do*, and they're
> highly optimized to do it as quickly as possible (people are endlessly
> comparing browsers' page rendering times). They can do it working
> directly on their internal structures, whereas if you create and
> append elements via DOM methods, you're working through a third-party
> API layered on top of the browser's internals.

I think that is missguided. The source of both HTML and DOM
specifications is the W3C, so the same "third-party" for both. In the
case of innerHTML, the string must first be parsed, then browser-
native methods called. The DOM methods should have direct access to
those methods, and therefore should be at least as fast. In modern
browsers, innerHTML and DOM are about the same speed.

> No surprise, then,
> that the browser does it faster when you let it use its native
> implementations of things. :-)

Big surprise, actually, since they are both "native" to the browser.
In the days when Microsoft was developing IE 6, they don't seem to
have had any interest in supporting W3C methods efficiently, but made
their proprietary methods quite fast. Other browsers followed suit so
as not to be left behind. In Safari (and likely other WebKit based
browsers), DOM and innerHTML have always been pretty much the same
speed, with one faster than the other depending on the version.


> I did a similar exercise a while back (here[1]), comparing DOM methods
> vs. Prototype's wrappers for DOM methods vs. concatenating a string
> and setting innerHTML. The last method, innerHTML, wins on every
> browser I tried, hands down, usually by at least one order of
> magnitude.

"An order of magnitude" suggests some power of 10. I don't see it,
even in IE, unless you use very inefficient code (see below). A
factor of 4 or 5 perhaps, for IE, and 1 to 3 for others.

> (Prototype's wrappers aren't very costly,

They are more costly in IE than any other browser (the $ function adds
50-odd properties to elements every time it's called) making its DOM
performance even worse.

> but going
> through the DOM methods is, and so they inherit that cost.)

Sorry, that makes no sense. Prototype.js is one of the slowest modern
"frameworks"[1]. If you mean calling Prototype.js functions as
wrappers for innerHTML is faster than plain old javascript (POJS),
then you may have a point in IE, but not in other browsers, and your
test code doesn't show it.

> The
> smallest discrepancy, IIRC, is on Chrome ("only" about four times
> faster) -- but then, Chrome is freakishly fast at nearly
> everything. :-)
>
> [1]http://pastie.org/476791

The comment in the code that you can't use the DOM to construct a
table in IE is false.

The POJS code is inefficient, it is even dependent on Prototpye's
insert() method, try the updated function below. For 100 rows and
cells, I get:

Firefox:
DOM took 772ms
[Prototype] DOM took 8505ms
HTML took 374ms

IE 6 gives:
DOM took 2109ms
[Prototype] DOM took 40168ms
HTML took 3125ms

That's right, DOM is *faster* than your HTML method in IE 6. Note the
increadibly slow performance for Prototype.js.

I don't have Safari or Chrome available right now, but I'll bet
they're both significantly faster than Firefox or IE 6.

var numRows = 100;
var numCols = 100;

function useDOMDirect() {
var start = new Date();
var table = document.createElement('table');
var tbody = document.createElement('tbody');
var otr = document.createElement('tr');
var otd = document.createElement('td');
var frag = document.createDocumentFragment();
table.appendChild(tbody);

for (var row = 0; row < numRows; row++) {
tr = otr.cloneNode(false);
frag.appendChild(tr);
for (var col = 0; col < numCols; col++) {
td = otd.cloneNode(false);
td.appendChild(document.createTextNode('Row ' + row + ',
col ' + col));
tr.appendChild(td);
}
}
tbody.appendChild(frag);
document.getElementById('targetTable').appendChild(table);
var end = new Date();
log('DOM took ' + (end - start) + 'ms');
}


1. <URL: http://dante.dojotoolkit.org/taskspeed/ >


--
Rob
Reply all
Reply to author
Forward
0 new messages