The Snap team is excited to announce the release of Heist 0.10. For this massively backwards-incompatible release we went back to the drawing board and re-wrote Heist from the ground up with performance in mind. But was Heist actually slow? Nobody has actually complained about it, but yes, Heist was slow. Well…it was until now.
Note that this chart is log scale. The difference is so drastic that if the chart was linear scale, the bars for Heist 0.10 wouldn’t even show up! The three pages benchmarked are taken directly from snapframework.com, so they are real-world examples. Heist 0.10 gives an improvement of more than 3000x for snaplets.tpl, 2000x for about.tpl, and 700x for faq.tpl. The improvement for faq.tpl is smaller because most its content is statically generated by pandoc, so Heist only has to traverse a small DOM compared to the other two templates.
To understand what happened here, we need to go back to the beginning. When we originally wrote Heist, speed was not our goal. Instead we wanted to see how far a simple concept like binding Haskell code to HTML nodes could take us in solving the problem with HTML boilerplate. Now, two and a half years later, we’ve discovered that it’s an incredibly powerful and enjoyable model for web programming.
Heist achieves this by traversing the DOM, executing splices to generate replacement nodes, and recursively traversing those nodes until all splices have been executed. A significant part of Heist’s power lies in the fact that this transformation happens at runtime every time a template is served, so the splice computations are running in your web server monad and have access to all the data from the HTTP request. Once this transformation is complete, the resulting DOM is rendered to a ByteString and served back in the HTTP response. This is really slow when compared with concatenative template systems like StringTemplate that just blast out interleaved static and dynamic chunks of data. Therefore it should be no surprise that splicing is slow, as it allows for much more expressiveness and power.
However, we realized that a lot of the transformations could be done at load time and preprocessed to an intermediate representation. This consists of static ByteStrings interleaved with deferred dynamic computations that still give you the power of being able to access the web server’s runtime monad. This “compiled splice” as we call it works quite a bit differently from the old model of splices. It is also strictly less powerful, so we kept the ability to use the old “interpreted splice” model if you really need the power and can afford the performance penalty.
While we were making backwards incompatible changes, we decided to clean up everything. This included reorganizing the modules and any other changes we felt would improve the overall quality and readability of the code base. It will be a bit of work to migrate existing code to Heist 0.10, but the old interpreted splices are still available and allow you do a gradual transition. We also added a new feature called attribute splices that allows you to abstract attributes in ways that weren’t possible before.
For more detailed information about the new features, check out the tutorials in the heist section of our docs page. After you have read those and are ready to upgrade your site, you might want to follow our migration guide to help you with the transition to 0.10. The migration guide is a github wiki, so feel free to help us make it clearer and more complete.