Is the &Method(:Method_Name) Idiom Bad for Performance in Ruby

Is the &method(:method_name) idiom bad for performance in Ruby?

Yes, it appears to be bad for performance.

def time
start = Time.now
yield
"%.6f" % (Time.now - start)
end

def do_nothing(arg)
end


RUBY_VERSION # => "1.9.2"

# small
ary = *1..10
time { ary.each(&method(:do_nothing)) } # => "0.000019"
time { ary.each { |arg| do_nothing arg } } # => "0.000003"


# large
ary = *1..10_000
time { ary.each(&method(:do_nothing)) } # => "0.002787"
time { ary.each { |arg| do_nothing arg } } # => "0.001810"


# huge
ary = *1..10_000_000
time { ary.each(&method(:do_nothing)) } # => "37.901283"
time { ary.each { |arg| do_nothing arg } } # => "1.754063"

It looks like this is addressed in JRuby:

$ rvm use jruby
Using /Users/joshuajcheek/.rvm/gems/jruby-1.6.3

$ xmpfilter f.rb
def time
start = Time.now
yield
"%.6f" % (Time.now - start)
end

def do_nothing(arg)
end


RUBY_VERSION # => "1.8.7"

# small
ary = *1..10
time { ary.each(&method(:do_nothing)) } # => "0.009000"
time { ary.each { |arg| do_nothing arg } } # => "0.001000"


# large
ary = *1..10_000
time { ary.each(&method(:do_nothing)) } # => "0.043000"
time { ary.each { |arg| do_nothing arg } } # => "0.055000"


# huge
ary = *1..10_000_000
time { ary.each(&method(:do_nothing)) } # => "0.427000"
time { ary.each { |arg| do_nothing arg } } # => "0.634000"

Is there a more concise way to call an outside method on a map in Ruby?

Yes, possible this way, but not good for performance (see post: Is the &method(:method_name) idiom bad for perfomance in Ruby?):

targets = ['./foo', './bar', './free']
targets.map(&Dir.method(:exists?))
# => [false, false, false] #all are false,as I don't have such directories.

Dynamic method calling in Ruby

is there any reason to ever use send?

call needs a method object, send doesn't:

class Foo
def method_missing(name)
"#{name} called"
end
end

Foo.new.send(:bar) #=> "bar called"
Foo.new.method(:bar).call #=> undefined method `bar' for class `Foo' (NameError)

is there any reason to ever use eval?

eval evaluates arbitrary expressions, it's not just for calling a method.


Regarding benchmarks, send seems to be faster than method + call:

require 'benchmark'

class Foo
def bar; end
end

Benchmark.bm(4) do |b|
b.report("send") { 1_000_000.times { Foo.new.send(:bar) } }
b.report("call") { 1_000_000.times { Foo.new.method(:bar).call } }
end

Result:

           user     system      total        real
send 0.210000 0.000000 0.210000 ( 0.215181)
call 0.740000 0.000000 0.740000 ( 0.739262)

Shorter way to pass every element of an array to a function

You can do this

@files.map(&method(:read))

But be aware though about performance.

Is there a ruby idiom for returning the first array element, if only one exists?

You seem to want to handle the case of val array being undefined so...

val.size > 1 ? val : val[0] if defined?(val)

But as has been pointed out it would be better to deliver a consistent argument (always arrays) so the following will deliver the val array or an empty array if undefined

defined?(val) ? val : []

Which gives me better performance Activerecord find_by or where?

where gives you marginally better performance. find_by is implemented through method_missing magic; method_missing requires a call to the object's method table to see if there's an existing method first, and if not then a call to method_missing, whereas where just finds the method correctly.

To see actual benchmarks of this, check out this post.

does ruby have an elegant way to say array2 = some_lookup_method(array1)

You can use the map command, which will return a new array with the results of your code block:

long_code = short_code.map{ |code| my_lookup_long_code(code) }

How do I pass an argument to array.map short cut?

You can't do it like this. The & operator is for turning symbols into procs.

a = [1, 2, 3, 4, 5]  
puts a.map(&:to_s) # prints array of strings
puts a.map(&:to_s2) # error, no such method `to_s2`.

& is a shorthand for to_proc:

def to_proc
proc { |obj, *args| obj.send(self, *args) }
end

It creates and returns new proc. As you see, you can't pass any parameters to this method. You can only call the generated proc.



Related Topics



Leave a reply



Submit