Read more

There is no real performance difference between "def" and "define_method"

Arne Hartherz
July 19, 2016Software engineer at makandra GmbH

You can define methods using def or define_method. In the real world, there is no performance difference.

Illustration web development

Do you need DevOps-experts?

Your development team has a full backlog? No time for infrastructure architecture? Our DevOps team is ready to support you!

  • We build reliable cloud solutions with Infrastructure as code
  • We are experts in security, Linux and databases
  • We support your dev team to perform
Read more Show archive.org snapshot

define_method is most often used in metaprogramming, like so:

define_method :"#{attribute_name}_for_realsies?" do
  do_things
end

Methods defined via define_method are usually believed to have worse performance than those defined via def.
Hence, developers sometimes prefer using class_eval to define methods using def, like this:

class_eval "def #{attribute_name}_for_realsies?; do_things; end"

You can benchmark methods defined like this and will see that those defined via def actually do perform better. Basically, it ranks like this:

  1. def in your class' Ruby code
  2. def inside class_eval
  3. define_method

However, there is only significant difference for really short (or empty) methods. Once you add a bit of logic, all perform almost equally:

require 'benchmark/ips' # requires the 'benchmark-ips' gem

GC.disable

class Foo
  define_method("foo") { 10.times.map { "foo".length } }
  class_eval 'def bar; 10.times.map { "foo".length }; end'
  def baz; 10.times.map { "foo".length }; end
end

Benchmark.ips do |x|
  foo = Foo.new
  x.report("define_method") { foo.foo }
  x.report("def via class_eval") { foo.bar }
  x.report("def") { foo.baz }
end
Warming up --------------------------------------
       define_method    42.579k i/100ms
          class_eval    44.781k i/100ms
                 def    44.823k i/100ms
Calculating -------------------------------------
       define_method    512.248k (± 0.8%) i/s -      2.597M in   5.070730s
  def via class_eval    534.132k (± 0.5%) i/s -      2.687M in   5.030474s
                 def    535.329k (± 0.6%) i/s -      2.689M in   5.023994s

There is a bit more to all that, so I suggest you read the attached article by @tenderlove.
I could confirm his observations on Ruby 2.2 and 2.3, too.

All in all, don't worry about having define_method for your application's bit of metaprogramming.
It may matter for a framework like Rails, and for methods that are called really often, but that is not your everyday code.
Do keep in mind though that define_method's closures can prevent objects from being garbage collected.


Update in 2020: Note that, starting with Ruby 2.5, the performance gap between define_method and def actually increased a little, to a difference of about 5-10% for the example above (tested for 2.5.7, 2.6.5, 2.7.0). However, the above still applies: for your few lines of meta programming, you won't ever notice any difference. If you want to improve your code's performance, this is not the place to start.

Arne Hartherz
July 19, 2016Software engineer at makandra GmbH
Posted by Arne Hartherz to makandra dev (2016-07-19 11:35)