coffeekup icon indicating copy to clipboard operation
coffeekup copied to clipboard

Optimize compiled templates

Open omarkhan opened this issue 13 years ago • 3 comments

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.

omarkhan avatar Oct 20 '11 21:10 omarkhan

Great work!

flosse avatar Nov 27 '11 03:11 flosse

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 :-)

wmertens avatar Mar 30 '12 08:03 wmertens

@wmertens

It does. Just type.

+1

gradus avatar Mar 30 '12 10:03 gradus