haml/haml — pull request #615 by dbussink (2013-01-04 14:20:58+00:00)
Title: Refactoring if internals to not flush inline caches at runtime

First of all, I don't expect this pull request to merged, I intend it to start a discussion since I'm not very familiar with the internals of Haml. It shows perhaps a way to start addressing the issue I'll explain further in this pull request.

When benchmarking Haml with both Rubinius and JRuby, it was found that some of the patterns in Haml have a very detrimental effect on performance. Probably MRI also suffers from this, although in a lesser amount.

The main issue here with the dynamic use of Object in https://github.com/haml/haml/blob/master/lib/haml/engine.rb#L122-L132. Every time Object#extend is called, it has to flush inline caches. This means also that JIT'ed code needs to be recompiled and never be properly optimized. Also the usage of dynamic instance variables is significantly slower than defining them beforehand. So in this case, an object that has an explicit accessor such as Haml::Scope in this pull request, which is for instance variables that are always used such as @haml_buffer beneficial.

What I'm wondering here is, whether it would be possible in Haml to make the code more optimization friendly. That means using less runtime changes to object types when possible. Perhaps there are situations where this can't be done, but I imagine that there should be a large number of cases where this can be improved.

For some benchmarks on the effect of Object#extend, also see here: http://tonyarcieri.com/dci-in-ruby-is-completely-broken. The biggest problem is that it doesn't affect code only locally. Especially in MRI, each Object#extend flushes all the inline caches for every method call in the system, which is a very severe performance penalty here.