Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optimize compiled templates #87

Open
wants to merge 59 commits into
base: master
from
Open

Conversation

@omarkhan
Copy link

omarkhan commented Oct 20, 2011

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.

@flosse
Copy link

flosse commented Nov 27, 2011

Great work!

@wmertens
Copy link

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

@gradus
Copy link

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
Projects
None yet
Linked issues

Successfully merging this pull request may close these issues.

None yet

4 participants
You can’t perform that action at this time.