Performance of Dynamic Code Invokation
In my new project I need to surround basic method with some wrapping code, which should be called dynamically, e.g. not each time, but under certain circumstances. In a first implementation by Christian Neukirchen this was done by adding the code in private methods and substituting the core method with code, that calls the other ones. Simple and charming.
But I will try a different approach. More details will follow later. For now I need to now, how to inject new code into a class or better a single object as fast as possible. To get the right feeling, a simple message send to the object will act as basic measuring unit.
Implementations
I came up with these options:
- wrap your code into a block and call
instance_eval
on the object - wrap your code into a string and call
instance_eval
on the object - wrap your code into a method within that class and fetch the corresponding
UnboundMethod
. Bind thatUnboundMethod
to the object and execute it - a variation of the former, just include the binding into the bechmark
These result in the following code:
require 'benchmark'
class A
def a
true
end
end
n = 1_000_000
Benchmark.bm(30) do | b |
instance = A.new
b.report( "simple call" ) do
n.times { instance.a }
end
instance = A.new
block = lambda { true }
b.report( "instance_eval with block" ) do
n.times { instance.instance_eval( &block ) }
end
instance = A.new
string = "true"
b.report( "instance_eval with string" ) do
n.times { instance.instance_eval( string ) }
end
method = A.instance_method( :a )
instance = A.new
bound_method = method.bind( instance )
b.report( "bound method" ) do
n.times { bound_method.call }
end
method = A.instance_method( :a )
instance = A.new
b.report( "bind method each time" ) do
n.times { method.bind( instance ).call }
end
end
That was easy…
And now the results
I tested it on Ruby 1.8.6 and Ruby’s YARV trunk (2007-04-12) and JRuby trunk (2007-03-31) on my MacBook. First of all the numbers:
Ruby 1.8.6 (MRI) | Ruby 1.9 (YARV) | JRuby | |
---|---|---|---|
simple call | 1.0 (0.42)* | 1.0 (0.25) | 1.0 (1.40) |
eval with block | 2.5 (1.08) | 6.3 (1.57) | 1.6 (2.28) |
eval with string | 7.3 (3.06) | 48 (12.10) | 26.5 (37.15) |
bound method | 1.2 (0.49) | 1.8 (0.45) | 1.2 (1.73) |
bind method each time | 2.7 (1.15) | 4.3 (1.08) | 3.1 (4.35) |
To make it clear, I did not compare the performance of MRI with YARV or JRuby - this is done elsewhere. I also did not optimize JRuby’s perfomance with some environment variables (see this article to see what should be done). First of all, I wouldn’t know which and then it wouldn’t change much, because I only compare JRuby with itself. I was only interested in the fastest implementation strategie.
And the winner is bound method. When I am able to cache the bound methods for all objects, then I have nearly no performance impact related to the inclusion of the “foreign code”.
Lessons learned
Do not call instance_eval
in YARV or JRuby with a String regularly. No, no,
no. It looks evil and it is really slow. The only advantage is that no closure
has to be stored so it may be useful for light dynamic method definitions.
)* The numbers in brackets are seconds, the absolute results printed to stdout.

My name is Gregor Schmidt. I am a freelance Ruby and JavaScript web developer based in Berlin, Germany. I do Ruby and Rails since 2005, JavaScript since 2006. I wrote my first Redmine plugin in 2007.
I mainly work with Rails, Backbone, and Bootstrap, but I am also good at picking up new frameworks, since I will probably know most of their concepts from other projects.
If your interested in more of my previous work have a look at my portfolio. I have also published my rates for everybody to see. I would love to hear, how I may help you.