Six Quick Optimization Tips for the Ruby Interpreter
Ilya Grigorik of Igvita has put together a great article called 6 Optimization Tips for Ruby MRI. In it, he walks through six different things you can do to improve the performance of your code when running on the regular Ruby interpreter (MRI). For example, interpolation is quicker than concatenation when working with strings, destructive operations prevent wasteful object duplication, and hand-written blocks are a lot faster than Symbol#to_proc.
And.. metaprogramming, of course, comes in for some much deserved performance criticism:
Ruby has a wonderful property of being highly dynamic, which in turn, allows us to create all kinds of spectacular meta-programming scenarios. However, this comes at a price of minimal runtime and compile time optimization (JRuby and some other VM's are changing this). Unlike most other languages, Ruby's MRI does not generate bytecode (Ruby 1.9 will change this), but relies solely on searching through the Abstract Syntax Tree (AST) for virtually every method call, variable, and so on. Sounds expensive? It is. Hence, next time you're saving yourself three lines of code by writing a meta function, consider inlining the logic directly.
From personal experience, I know there are a lot of alternative coding techniques you can use to shave off valuable milliseconds here and there in Ruby. If you have any to share, post a comment, and I'll be putting together my own compilation of tips on Ruby Inside in the near future.
July 11, 2008 at 4:32 am
Surely if we wanted to shave off valuable milliseconds we'd be using C rather than Ruby. Rubyists optimize their code for beauty (and maintainability) rather than speed or size.
July 11, 2008 at 7:23 pm
Cool! I didn't know about the benchmarking extension (I was using Time.new.to_f so far).
I've noted that Range::min and Range::max are very slow. Just try to execute the following line of code:
(1...2**22).max
For integers it is better to use Range::begin and Range::end-1. You can also simply override the methods like this:
class Range
alias_method :orig_min, :min
alias_method :orig_max, :max
def min
if Integer === self.begin
self.begin
else
orig_min
end
end
def max
if Integer === self.end
exclude_end? ? self.end - 1 : self.end
else
orig_max
end
end
end