Thursday, March 29, 2012

Two methods for optimize performance for finding comments for template usage in html-documents

Ok, so we are using comments in our project as placeholders for our templating system. We want to do that, since we don’t want to add unnecessary elements to our DOM-tree.
However, finding comments is not as easy as finding for example a div with an ID, since they aren’t a part of the DOM model. And that was exactly why we want to use them in the first place, remember!? J
So how do we find comment elements? Well, one way as we found was to traverse the document using jQuery, but instead of using children() we use contents(). Contents return all content, including comments, making it possible to find them.
However, finding an object in a document-tree manually, by traversing the elements one by one, is a very expensive operation that scales badly. That’s not good, since we need our site and code to be fast no matter how large our page is.

XPath

So what can be done? Well, it turns out that Firefox and Chrome supports xpath lookups, making the comment finding extremely easy. All you have to do is to use document.evaluate together with comment(): 

But this doesn’t work on IE. Too bad, since it’s extremely fast.
However, we know something about our problem. We aren’t interested in just any comments, but comments to be used with our template system. That means that the comments that we are looking for are more likely to be found close to the root of the document tree. In our first implementation we did a traditional, recursive traverse of the document tree, going from root to branch and leaf, one at a time, finishing the previous branch before starting in the next.
That’s not optimal. For the footer templates that mean that we need to traverse through almost the full tree every time before we find it.

Horizontal versus vertical traversing 

The solution then is to traverse the tree one level at the time, starting with looking for the comments one level down from the document root, then two levels down, then three, etc. But however much faster that it, it still means a lot of tree traversal. And what can we do then? 
Cache

Of course, as we all know, the answer is cache. We want to make the comment easy to find. We can’t really store the comment itself, because we can’t insert DOM elements after it. But what we can do is to save the comment’s parent’s location. That way, the next time we need to find the comment, we will start looking directly at the right branch.
Of course, if the templates changes position, if a template placeholder is within another, the comments we’re after might move, or their parents being removed. That’s easy enough to solve; If that happens, we start a new, smart search.

Results

So what are the results of the smart document tree traversal and caching? A great speedup, actually, that gets better as the DOM grows. For a tiny hml-document our optimized version where 40% faster, for a small page (~600 elements in the DOM-tree) the speedup was 150%, and for a normal sized page (~1500 elements) the speedup was 700%!
That’s a good result. Also, as long as the template comments are close to the document root, the optimized version will scale perfectly no matter how large the document is.

(I did this as a part of a project here at Betsson. We do great stuff here and have a lot of fun as well, so check out Betsson's open positions!)

Tuesday, March 27, 2012

A quick comparisson of performance when creating DOM elements using Javascript

Over lunch we discussed the difference in performance when creating DOM elements with native javascript as compared to using jQuery, and the difference in execution time for creating elements to simply reusing old ones.

To test this, we created a short snippet where we added and removed div elements to the body 10,000 times. We created four different cases.

  1. Create everything with jQuery, lookup for elements to be removed using jQuery.
  2. Create everything with jQuery, but cache the elements so that the extra lookup is avoided.
  3. Create everything with jQuery, but instead of deleting objects, save them and reuse them.
  4. Do everything with native xml functions (createElement, removeElement etc), use cache and reuse elements.

I would have expected that the biggest difference would be between case 1 and 3, since object creation is an "expensive" operation.

The results looked like this:

BrowserCase 1Case 2Case 3Case 4
Firefox 111569ms1359ms1105ms496ms
Chrome 17837ms548ms366ms78ms

The results were kind of surprising. Reusing elements are efficient, but not the kind of performance booster as it is in Flash or Cocoa. But what are very clear are two things: jQuery's DOM append is very expensive, and Chrome are a lot faster than Firefox. On Chrome the difference between the original script and the "optimized" case was especially large, with a performance boost of 10 times!

(I did this as a part of a project here at Betsson. We do great stuff here and have a lot of fun as well, so check out Betsson's open positions!)

Monday, March 19, 2012

The All Mighty Johan Ripås-plugin

Jag funderar på om man skulle ta sig an det här med att länka till Johan Ripås på ett lite mer effektivt sätt. Kanske rent av skriva ett plugin till Wordpress. Med några enkla knapptryckningar skulle man kunna få in en länk till Johan Ripås hemsida på alla hemsidor som installerade pluginet.

Hur utvecklar man då ett sånt plugin? Jo, det första man gör är att skapa ett bibliotek i sin wordpress-installation (givetvis utvecklingsvarianten) under wp-content/plugins med namnet johan_ripas.
I denna placerar vi två filer: readme.txt och johan_ripas.php.
I readme.txt skriver vi det vanliga:

=== Johan Ripås ===
Contributors: Aspelund
Tags: Johan Ripås
Requires at least: 3
Tested up to: 3
Stable tag: 3

This plugin adds links to support the cause of Johan Ripås. Nothing more, nothing less.

== Description ==

This is a simple plugin that adds two links to your blogs footer.
One link to [Johan Ripås](http://www.lindqvist.com/tag/johan-ripas/) and on link to [Johan Ripås](http://johanripas.wordpress.com/).
It is not likely that this plugin will ever be updated.

== Installation ==

Use Wordpress' own installation process in the admin guide.

== Changelog ==



I filen johan_ripas.php skapar vi sedan koden som lägger till länkarna till Johan Ripås och Johan Ripås. Vi hookar upp dem mot actionen wp_footer.

class johanripas {
    function add_johan_ripas_link()  {

        echo '
Jag länkar glatt till Johan Ripås och Johan Ripås.
';     } } add_action('wp_footer',array('johanripas','add_johan_ripas_link')); ?>

Notera att vi lägger koden inuti klassen johanripas. Det innebär att vi undviker namnkonflikter. :)
När detta är gjort har vi vår plugin, och det är bara att aktivera den från admin-sidan.

För att göra det lite lättare så har jag sparat det hela som en färdig fil: Johan Ripås - The plugin.