Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

why a tiny reordering of DOM Read/Write operations causes a huge performance difference

the following code illustrate the problem, changing the order of Read/Write causes a big difference in execution time (Tested using Chrome, Firefox and IE) :

// read->write->read->write...
function clearSlow(divs){
    Array.prototype.forEach.call(divs, function(div) {
        contents.push(div.clientWidth);
        div.style.width = "10px";
    });
}
// read->read->...->write->write...
function clearFast(divs){
    Array.prototype.forEach.call(divs, function(div) {
        contents.push(div.clientWidth);
    });
    Array.prototype.forEach.call(divs, function(div) {
        div.style.width = "10px";
    });
}

Here's a JSFiddle for the complete example http://jsfiddle.net/Dq3KZ/2/ .

My results for n=100 :
Slow version : ~35ms
Fast version : ~2ms

for n=1000:
Slow version : ~2000ms
Fast version : ~25ms

I think this is related with the number of browser reflow in each case. In the slow scenario, a reflow happens for each write operation. However, in the fast scenario, the reflow occurs only once at the end. But I'm not sure and I don't understand why it does work that way (when the operations are independent).

Edit: I used InnerText property instead of clientWidth and Style.Width , I got the same behavior when using Google Chrome (http://jsfiddle.net/pindexis/CW2BF/7/). However, when InnerHTML is used, there's almost no difference (http://jsfiddle.net/pindexis/8E5Yj/).

Edit2: I have opened a discussion about the innerHTML/innerText issue for those interested: why does replacing InnerHTML with innerText causes >15X drop in performance

like image 262
Amine Hajyoussef Avatar asked Oct 08 '13 14:10

Amine Hajyoussef


1 Answers

The operations are not independent.

A reflow must occur when the style or content sizes changed and positions or dimensions are needed, either because you request a dimension or because the screen must be updated (when your code finishes).

div.clientWidth is a hidden function call. When you request this value, you request in fact an up to date value and thus, as the style changed, you trigger an immediate reflow.

In the "fast" case, there's no reason to do a reflow until the screen is redrawn or you request a size. Only one reflow is really needed in this case.

like image 164
Denys Séguret Avatar answered Nov 11 '22 09:11

Denys Séguret