Performance characteristics of Ruby constructs

Hi.

I’m right in the situation where I would like to find out some
performance characteristics for various parts of Ruby, to optimize an
application. Is there any resources available for this?

To make it concrete, let’s take two examples.

I’ve recently written a hybrid state-based, table-driven LL(1) parser,
where I gather productions (in the form of symbols) on a stack, and then
execute these with send(), until a get back the next terminal. This
works really great, but right now I’m wondering how cheap send() is,
compared to using a real table-driven approach.

Next example: there’s fairly heave use of Class#=== throughout the
application, and the profiler says I spend much time there. It’s a
fairly small class of Class comparisons I’m doing, though, so maybe it
would get me better performance by polluting the global namespace like
this:
class Object
def __is_directive_token?; false; end
def __is_sequence_start_token?; false; end
end
class Token
end
class DirectiveToken
def __is_directive_token?; true; end
end
and just do tok.__is_directive_token? instead of DirectiveToken === tok.

Any thoughts in this? Is there any place to find general information
this, or I’m out in the wild right now?

Regards
Ola B.

On May 22, 2006, at 10:13 PM, Ola B. wrote:

I’m right in the situation where I would like to find out some
performance characteristics for various parts of Ruby, to optimize an
application. Is there any resources available for this?

The built-in profiler, Shugo’s Prof and ZenHacks’ zenprofile are all
great for this. Oh, I almost forgot benchmark.rb, also built-in.

To make it concrete, let’s take two examples.

I’ll skip straight to the second example.

Next example: there’s fairly heave use of Class#=== throughout the
application, and the profiler says I spend much time there.

Since you already know how to use the profiler you should be able to
iteratively test small changes to make optimize either problem
correctly. Propose a change, make it, test its effectiveness, revert
if it doesn’t help, repeat.

end

class DirectiveToken
def __is_directive_token?; true; end
end

and just do tok.__is_directive_token? instead of DirectiveToken ===
tok.

(This machine has a 266MHz Geode CPU, so forgive the poor numbers.)

$ cat bm.rb
require ‘benchmark’

class Object
def directive_token?
false
end
end

class DirectiveToken
def directive_token?
true
end
end

o = Object.new
dt = DirectiveToken.new
n = 1_000_000

Benchmark.bmbm do |bm|
bm.report ‘#diretive_token?’ do
n.times { o.directive_token?; dt.directive_token? }
end

bm.report ‘#===’ do
n.times { DirectiveToken === o; DirectiveToken === dt }
end
end

$ ruby -v bm.rb
ruby 1.8.2 (2004-12-25) [i386-freebsd5]
Rehearsal ----------------------------------------------------
#diretive_token? 15.226562 0.156250 15.382812 ( 16.111389)
#=== 18.054688 0.117188 18.171875 ( 19.082567)
------------------------------------------ total: 33.554688sec

                    user     system      total        real

#diretive_token? 15.625000 0.125000 15.750000 ( 16.463518)
#=== 17.617188 0.171875 17.789062 ( 18.649016)

Any thoughts in this? Is there any place to find general
information this, or I’m out in the wild right now?

My biggest question is: Why do you need to do all those class
comparisons? Perhaps you can change your design to increase speed.

Also, if small enough, posting your code may get the local parser
geeks proposing all sorts of tuning tips.


Eric H. - [email protected] - http://blog.segment7.net
This implementation is HODEL-HASH-9600 compliant

http://trackmap.robotcoop.com

Hi!

Thank you for the help.
I had forgot about the builtin benchmark.rb, actually. But what I was
looking for was more of the kind of information where I can read about
this, instead of trying different ways to get what I want. The big
problem
is that most of the code doing stuff (like the Class#===) is distributed
in
many places, and if I’m going to replace it, it would be good to find
out
beforehand if it’s worth it, especially when just replacing one point
won’t
show that much difference in the performance.
Actually, replacing the Class#=== with predicates on classes gave me
about
5% in performance, in this limited domain.

The class comparisons are right now done, because it’s an LL(1) parser,
and
the lookahead checks the type of the next token to determine which
action
to take.

I probably won’t post the code directly to the list, but when I’ve
optimized it sufficiently it will go into the next release of RbYAML,
and
I’ll ask the parser geeks to take a look at it. =)

Regards
Ola

On 5/23/06, Ola B. [email protected] wrote:

Hi!

Thank you for the help.
I had forgot about the builtin benchmark.rb, actually.

I don’t know of any good general info on speed of different Ruby
constructs (there is some in the “Ruby Developers Guide” but probably
a bit dated now) but if you decide to use benchmarking the attached
benchmarking code might be more useful than the built-in one. It is
based on code by Mauricio F. to ensure you have a certain
confidence in the measured time results. I simply added a ranked
print-out of the times. There is a todo in the comment of things that
should be added to make it even more useful (I had code for doing this
way back when involved in writing a Ruby book but lost it in a HD
crash and haven’t had the need for it since then).

Anyway if this is useful feel free to use it (and please blog about /
publish any generally applicable performance results).

Regards,

Robert F.

Anyway if this is useful feel free to use it (and please blog about /
publish any generally applicable performance results).

BTW, you should definetely use bmbm instead of bm…

Regards,

Robert

2006/5/23, Ola B. [email protected]:

Actually, replacing the Class#=== with predicates on classes gave me about
5% in performance, in this limited domain.

The class comparisons are right now done, because it’s an LL(1) parser, and
the lookahead checks the type of the next token to determine which action
to take.

I think Eric is right in that you should rethink your design. Usually
in OO languages you use method overloading to get proper behavior. A
fast alternative in your case might be to use a hash with blocks.

action[ next_token.class ].call( whatever, arguments )

Note though that this has slightly different semantics as === will
check for subclasses also while the hash approach will only tackle
individual classes - unless you add some magic to the hash like this:

action = Hash.new {|h,cl| cl ? h[cl.ancestors[1]] : lambda {“fallback”} }
=> {}
action[Enumerable] = lambda { “foo” }
=> #Proc:0x100d0948@:68(irb)
action[String].call
=> “foo”
action[Fixnum].call
=> “fallback”

Cheers

robert