Sunday, May 6, 2007

Delayed execution idea scrapped, but the optimizations stayed

After some more thoughts, delayed execution is found to be stupid. It requires the web developer to change their code to get the benefits, and it sometimes breaks your application.

What's this delayed execution stuff about originally? It's actually a trick to get around browser inefficiencies in rendering DOM nodes with CSS attributes.

Consider the following two code snippets:

// placing 5000 "Hello World" messages in random positions
for(var i=0;i<5000;i++)
{
var node = document.createElement("div");
node.appendChild(document.createTextNode("Hello World!"));
document.body.appendChild(node);
node.style.position = "absolute";
node.style.left = parseInt(Math.random() * 800) + "px";
node.style.top = parseInt(Math.random() * 800) + "px";
}

// placing 5000 "Hello World" messages in random positions
for(var i=0;i<5000;i++)
{
var node = document.createElement("div");
node.appendChild(document.createTextNode("Hello World!"));
node.style.position = "absolute";
node.style.left = parseInt(Math.random() * 800) + "px";
node.style.top = parseInt(Math.random() * 800) + "px";
document.body.appendChild(node);
}

Both code snippets place 5000 randomly positioned "Hello World!" messages in the browser window. The two code snippets differ only in the placement of the document.body.appendChild() line. Running the first code snippet in Firefox can take 1 minute or more, but running the second one takes only a few seconds. The second code snippet provides a more than 10x speedup compared to the first code snippet.

Similar phenomenon can be observed in Internet Explorer also, but only with much more complicated logic, so we'll not go over that. But anyway, the moral of the story is, modifying some CSS attributes (especially positioning attributes) is harmful after the DOM node is already visible.

So what did the scrapped delayed execution idea has to do with these browser weirdnesses? The delayed execution idea was meant to help in batch widget creation and batch CSS style manipulations. e.g. when you're creating 100 widgets in a single pass. It speeds up widget creation or CSS style manipulation by making a common ancestor DOM node of the widgets being manipulated/created invisible before executing the performance sensitive code, and making the ancestor node visible again after execution.

Sounds like a hack - yes it is a hack. It sometimes breaks your application code, it requires you to change your application code to use it. But as shown in the videos, it worked.

Now, the hack is scrapped, before it is even released. And that's because we've got a more consistent way of implementing the same optimization in WT Toolkit, without the need of using hacks.

So what do we have for 0.3.3 now:

  1. Massively increased widget creation performance in Internet Explorer, without needing the developer to change a single line of code.
  2. No performance improvement in Firefox if you don't change your application code... Oops?! But hey, that would be the same if we implemented the delayed execution hack.

But what if you want to make your WT Toolkit application run faster in Firefox? Just pass null as the parentWidget argument to the widget constructor as much as possible, and add the widget to the document tree only after you've done all the CSS manipulations.

Say, if you have

var n = new wtButton(myParent, "Yes!");
n.setAbsolutePosition(x, y);

Then, the optimized version would be

var n = new wtButton(null, "Yes!");
n.setAbsolutePosition(x, y);
myParent.addWidget(n);

Actually, you can perform the manual optimization with WT Toolkit 0.3.2 too.

0 comments: