Optimize compiled templates #87

Open
wants to merge 59 commits into
from

Conversation

Projects
None yet
4 participants
@omarkhan

I have been experimenting with optimizing coffeekup templates using UglifyJS to analyze the template source and prerender the html. The idea is to turn this:

function() {
  //
  // Lots of boilerplate code...
  //
  html(function() {
    head(function() {
      return title(this.title);
    });
    return body(function() {
      h1(this.title);
      return p('Super fast templates');
    });
  });}).call(data);return __ck.buffer.join('');
}

into this:

function() {
  //
  // Much less boilerplate
  //
  ((function() {
    text("<!DOCTYPE html>");
    text("<html>");
    text(function() {
      text("<head>");
      text(function() {
        return function() {
          text("<title>");
          text(this.title);
          text("</title>");
        }.call(data);
      }.call(data));
      text("</head>");
      return function() {
        text("<body>");
        text(function() {
          text("<h1>");
          text(this.title);
          text("</h1>");
          return function() {
            text("<p>");
            text("Super fast templates");
            text("</p>");
          }.call(data);
        }.call(data));
        text("</body>");
      }.call(data);
    }.call(data));
    text("</html>");
  })).call(data);return __ck.buffer;
}

The advantages of this approach are shorter template functions (unless the template is very long) thanks to reduced boilerplate, and faster execution owing to much of the work being done at compile time. A further performance improvement is achieved by using string concatenation rather than array join to build the output. Here are my benchmark results, running a compiled template 5000 times on node:

CoffeeKup (precompiled): 263 ms
CoffeeKup (precompiled, optimized): 24 ms
Jade (precompiled): 530 ms
haml-js (precompiled): 89 ms
Eco: 92 ms

See here for browser rendering performance: http://jsperf.com/coffeekup-optimized/2

As far as I can tell, these optimizations give a 10x performance boost on node, a 30x boost in Chrome and a more modest 3x improvement in Firefox.

Usage

template = coffeekup.compile 'h1 @title', optimize: yes
template(title: 'Super fast template')
# '<h1>Super fast template</h1>'

Caveats

As this compiler uses static analysis to optimize your templates, too much complex logic may cause it to fall over. But templates should be logic-free so that shouldn't be a problem, right?

I have aimed for API compatibility with regular coffeekup, but there are a few differences to be aware of:

  • The coffeescript helper function does not add "text/coffeescript" to the <script> tag. I could implement this, but it doesn't strike me as very useful.
  • Output formatting is not implemented (yet). You get a single line of html.
  • Arrays are not rendered directly in the template output. Join them into a string before passing them to the compiled template function.
  • You can't set up a hash of options and then pass it to the tag function, e.g.
attrs =
  name: 'email'
  type: 'text'
input attrs

Do this instead:

input
  name: 'email'
  type: 'text'

I have made some minor modifications to the test suite to account for the above.

Omar Khan added some commits Oct 17, 2011

@flosse

This comment has been minimized.

Show comment
Hide comment
@flosse

flosse Nov 27, 2011

Great work!

flosse commented Nov 27, 2011

Great work!

@wmertens

This comment has been minimized.

Show comment
Hide comment
@wmertens

wmertens Mar 30, 2012

I wish github had a +1 button, this is awesome! I know it's in https://github.com/gradus/coffeecup now but I just wanted to express my awe :-)

I wish github had a +1 button, this is awesome! I know it's in https://github.com/gradus/coffeecup now but I just wanted to express my awe :-)

@gradus

This comment has been minimized.

Show comment
Hide comment
@gradus

gradus Mar 30, 2012

@wmertens

It does. Just type.

+1

gradus commented Mar 30, 2012

@wmertens

It does. Just type.

+1

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment