I've been meaning to write and run this benchmark for a while to see how things fare in the view world these days. Initially I did markaby vs tagz (my favorite at a svelte 244 lines). I then added builder out of curiosity's sake. Thanks to atmos I was able to get code and numbers for erubis and haml.
Builder is slightly faster than tagz, but not enough for me to want to switch to it. Tagz and markaby read so much better than builder, but markaby is so much slower (4.73 times slower than builder) that I can't justify its use.
Now if only I could get tagz working correctly with sinatra. :(
# iterations = 10000
#
# user system total real multiplier
#
# builder 1.910000 0.000000 1.910000 ( 1.918751) (1.00)
# tagz 2.310000 0.020000 2.330000 ( 2.357710) (1.23)
# haml 5.370000 0.020000 5.390000 ( 5.418876) (2.82)
# erubis 5.390000 0.020000 5.410000 ( 5.446929) (2.84)
# markaby 8.980000 0.030000 9.010000 ( 9.078748) (4.73)
Code behind the cut...
Code:
#!/usr/bin/env ruby
require 'benchmark'
require 'rubygems'
require 'markaby'
require 'tagz'
max = (ARGV.shift || 1_000_000).to_i
def do_nothing
# do nothing
end
require 'haml'
require 'erubis'
def do_erubis
str = <<-EOF
<html>
<head>
<title>happy title</title>
</head>
<body>
<h1>happy heading</h1>
<a href='<%= 'url' %>'>a link</a>
</body>
</html>
EOF
eruby = Erubis::Eruby.new(str)
end
def do_haml
str = <<-EOF
%html
%head
%title_ "happy title"
%body
%h1 "happy heading"
%a "a link", :href => "url"
EOF
Haml::Engine.new(str)
end
def do_tagz
Tagz {
html_ {
head_ {
title_ "happy title"
}
body_ {
h1_ "happy heading"
a_ "a link", :href => "url"
}
}
}
end
def do_builder
Builder::XmlMarkup.new.html {
xm.head {
xm.title "happy title"
}
xm.body {
xm.h1 "happy heading"
xm.a "a link", :href => "url"
}
}
end
def do_markaby
mab = Markaby::Builder.new :output_meta_tag => false
mab.html {
head {
title "happy title"
}
body {
h1 "happy heading"
a "a link", :href => "url"
}
}.to_s
end
x = do_tagz
y = do_markaby
z = do_builder
raise "bad!\n\n#{x}\n\n#{y}" unless x == y
raise "bad!\n\n#{x}\n\n#{z}" unless x == z
puts "# of iterations = #{max}"
Benchmark::bm(20) do |x|
x.report("null_time") do
for i in 0..max do
do_nothing
end
end
x.report("tagz") do
for i in 0..max do
do_tagz
end
end
x.report("markaby") do
for i in 0..max do
do_markaby
end
end
x.report("builder") do
for i in 0..max do
do_builder
end
end
end

So I guess you’re not aware of Erector? Using this function for testing:
def do_erector Erector::Widget.new do html { head { title "happy title" } body { h1 "happy heading" a "a link", :href => "url" } } end.to_s endgave me these results:
# of iterations = 10000 user system total real null_time 0.000000 0.000000 0.000000 ( 0.005470) tagz 2.490000 0.060000 2.550000 ( 2.620850) markaby 9.520000 0.190000 9.710000 ( 10.032617) erector 1.470000 0.020000 1.490000 ( 1.524160)nope. I wasn't aware of erector. But there isn't a chance in the world that I'd ever pick it over tagz:
Heh, good point.
I think you should double-check your numbers... there's no way Erubis would be so slow. On my machine (macbook, 1.83Ghz) it takes 0.67 seconds to perform 10000 iterations (compared to ~7s for haml and ~13s for markaby).
I'm curious what happened that erubis was so slow for you - it's far and away the fastest of the bunch on my machine:
http://gist.github.com/65299
(and that's after I fixed the erubis code to actually render the html string, as well)
This isn't really a fair comparison. Haml and Erubis both take a significant amount of time just to parse their templates. In a real-world situation, the compiled Ruby code they generate would be cached. I've made a fixed benchmark here: http://gist.github.com/65305. It also fixes a couple errors in the Haml syntax.
Again, if you read my post, you'll see that I did not write or run the haml or erubis benchmarks. Atmos did. I don't care for haml or erubis and I don't actually care about their results. If you want to make changes, you're welcome to blog it. We can go back and forth until we have a complete list.
There's one library you missed that might be worth considering... Nokogiri. In provides a similar API to tagz, and is significantly faster in my benchmark.
http://gist.github.com/65516
brynary: that's actually really cool. I'll have to look into that tomorrow w/ Aaron. thanks.
@brynary - nokogiri absolutely kicks ass for reading, but it cannot currently generate tags like '' due to the implementation. when it does i'll have another look at it too. just fyi.
-a
@zenspider - what's the issue with sinatra?
I applaud your distaste for overly-complex plugins, but I think your use of wc was a wee bit too cavalier. Erector has one file, unicode.rb, that's automatically generated. It contains over 18000 lines like this:
Erector::CHARACTERS = { :space => 0x0020, :exclamation_mark => 0x0021, :quotation_mark => 0x0022,This is a performance optimization so we can do wicked fast entity escaping via a single method called 'character' and shouldn't really count towards any reasonable complexity metric. Without that file it weighs in at a svelte 1053 lines, plus 247 for Rails support and 96 for an optional table widget that nobody's really using anyway. We also have copious rdoc documentation so the actual LOC is much lower.
And it's fast too! I just added it to that benchmark and it's 10% faster than Tagz for the simple case, which doesn't count our optimizations around partials (we make sure to keep using the same output stream wherever possible to minimize string copy or realloc).
http://gist.github.com/115155
Tell me what you like most about Tagz. Is it the "include Tagz.globally" feature? Is it the ability to emit invalid HTML? Chances are we either do it already in Erector or it'll be trivial to add.
Here's my latest run from http://gist.github.com/115155 , adding loops and variables (so HAML doesn't get to cheat by just caching a static string).
# of iterations = 10000 user system total real tagz 13.740000 0.070000 13.810000 ( 14.064954) erector 6.500000 0.050000 6.550000 ( 6.744592) erector_mixin 6.390000 0.040000 6.430000 ( 6.508313) nokogiri 5.950000 0.040000 5.990000 ( 6.137619) builder 17.810000 0.090000 17.900000 ( 18.166922) haml 5.080000 0.030000 5.110000 ( 5.175749) erubis 0.400000 0.010000 0.410000 ( 0.411160) markaby 27.620000 0.130000 27.750000 ( 28.234580)Bottom line: Erubis is hella fast! And Markaby and Builder are slow. The rest are about the same. I wonder what tagz is doing that's making it drag behind the rest of the middle of the pack...