Performance question: Rational

I've got a ruby program that's running quite slowly. I ran the profiler to try to find out what's causing the hold up (see the profile output below) and it looks like Integer#gcd is a culprit. My code is basically a canvas with a bunch of canvas items and so it does some integer computation, but somehow integers are getting converted into rationals and then being simplified which appears to be taking up heaps of time.

Has anyone got any hints on how to keep rationals out of my program? I want to stick to integers and floats.

regards,

Richard.

···

-----
  % cumulative self self total
time seconds seconds calls ms/call ms/call name
2578.12 33.00 33.00 1 33000.00 33000.00 Profiler__.start_profile
21.87 33.28 0.28 72 3.89 5.97 Integer#gcd
  8.59 33.39 0.11 72 1.53 7.78 Rational#reduce
  8.59 33.50 0.11 144 0.76 10.28 Rational#/
  7.03 33.59 0.09 146 0.62 0.89 Rational#initialize
  6.25 33.67 0.08 1191 0.07 0.07 Fixnum#==
  3.91 33.72 0.05 661 0.08 0.08 Fixnum#[]
  3.13 33.76 0.04 582 0.07 0.07 Kernel.kind_of?
  3.13 33.80 0.04 363 0.11 0.11 Fixnum#>>
  3.12 33.84 0.04 326 0.12 0.21 Qt::Base#method_missing
  3.12 33.88 0.04 188 0.21 0.96 Class#new
  3.12 33.92 0.04 72 0.56 13.33 Fixnum#/
  2.34 33.95 0.03 6 5.00 16.67 Griff::CanvasMultiText#draw
  1.56 33.97 0.02 150 0.13 0.13 Fixnum#*
  1.56 33.99 0.02 114 0.18 0.18 Qt::Internal.find_class
  1.56 34.01 0.02 38 0.53 1.05 Qt::Pen#new
  1.56 34.03 0.02 364 0.05 0.05 Kernel.class
  1.56 34.05 0.02 18 1.11 2.78 Griff::CanvasLine#draw
  1.56 34.07 0.02 146 0.14 1.30 Rational#new!
  1.56 34.09 0.02 6 3.33 21.67 Griff::CanvasConceptLabel#draw
  1.56 34.11 0.02 72 0.28 8.06 Object#Rational
  0.78 34.12 0.01 12 0.83 3.33 Griff::CanvasTextStyle#fashion
  0.78 34.13 0.01 80 0.13 0.13 Fixnum#>
  0.78 34.14 0.01 36 0.28 26.94 Griff::Rect#center
  0.78 34.15 0.01 6 1.67 1.67 Griff::CanvasTextStyle#baseline
  0.78 34.16 0.01 6 1.67 5.00 Range#each
  0.78 34.17 0.01 218 0.05 0.05 Fixnum#<
  0.78 34.18 0.01 6 1.67 1.67 Qt::Enum#initialize
  0.78 34.19 0.01 6 1.67 1.67 Griff::CanvasTextStyle#left
  0.78 34.20 0.01 38 0.26 0.26 Kernel.callcc
  0.78 34.21 0.01 6 1.67 11.67 Griff::CanvasMultiText#drawText
  0.78 34.22 0.01 8 1.25 2.50 Griff::CanvasEllipse#draw
  0.00 34.22 0.00 2 0.00 0.00 Rational#to_f
  0.00 34.22 0.00 38 0.00 0.26 Qt::Internal.try_initialize
  0.00 34.22 0.00 6 0.00 0.00 Griff::CanvasTextStyle#margin
  0.00 34.22 0.00 6 0.00 3.33 Griff::CanvasRect#draw
  0.00 34.22 0.00 104 0.00 0.00 Fixnum#-
  0.00 34.22 0.00 6 0.00 0.00 Qt::Integer#initialize
  0.00 34.22 0.00 6 0.00 0.00 Griff::CanvasTextStyle#height
  0.00 34.22 0.00 1 0.00 0.00 Array#sort
  0.00 34.22 0.00 38 0.00 0.00 Kernel.method
  0.00 34.22 0.00 118 0.00 0.00 Hash#[]
  0.00 34.22 0.00 36 0.00 0.00 Griff::Point#initialize
  0.00 34.22 0.00 72 0.00 0.00 Fixnum#<<
  0.00 34.22 0.00 38 0.00 1.32 Griff::CanvasRectStyle#fashion
  0.00 34.22 0.00 42 0.00 0.00 Fixnum#to_f
  0.00 34.22 0.00 6 0.00 0.00 Array#[]
  0.00 34.22 0.00 94 0.00 0.00 Fixnum#+
  0.00 34.22 0.00 6 0.00 0.00 Qt::Internal.get_qenum_type
  0.00 34.22 0.00 6 0.00 1.67 Qt::Painter#const_missing
  0.00 34.22 0.00 2 0.00 0.00 Float#/
  0.00 34.22 0.00 1 0.00 1280.00 #toplevel
  0.00 34.22 0.00 6 0.00 0.00 Qt::Internal.get_qinteger
  0.00 34.22 0.00 4 0.00 12.50 Griff::Layer#draw
  0.00 34.22 0.00 2 0.00 0.00 Rational#coerce
  0.00 34.22 0.00 38 0.00 0.00 Qt::Base#initialize
  0.00 34.22 0.00 144 0.00 0.00 Fixnum#div
  0.00 34.22 0.00 144 0.00 0.00 Fixnum#abs
  0.00 34.22 0.00 5 0.00 18.00 Array#each
  0.00 34.22 0.00 1 0.00 0.00 Hash#keys
  0.00 34.22 0.00 38 0.00 0.00 Float#*
  0.00 34.22 0.00 6 0.00 1.67 Qt::Internal.create_qenum
  0.00 34.22 0.00 92 0.00 0.00 Float#+

well, just remove the line
require 'rational'
from your program.

best regards,

Brian

···

On 29/04/05, Richard Cole <rcole@itee.uq.edu.au> wrote:

I've got a ruby program that's running quite slowly. I ran the profiler
to try to find out what's causing the hold up (see the profile output
below) and it looks like Integer#gcd is a culprit. My code is basically
a canvas with a bunch of canvas items and so it does some integer
computation, but somehow integers are getting converted into rationals
and then being simplified which appears to be taking up heaps of time.

Has anyone got any hints on how to keep rationals out of my program? I
want to stick to integers and floats.

regards,

Richard.
[snip profiler output]

--
http://ruby.brian-schroeder.de/

multilingual _non rails_ ruby based vocabulary trainer:
http://www.vocabulaire.org/ | http://www.gloser.org/ | http://www.vokabeln.net/

Brian Schröder wrote:

I've got a ruby program that's running quite slowly. I ran the profiler
to try to find out what's causing the hold up (see the profile output
below) and it looks like Integer#gcd is a culprit. My code is basically
a canvas with a bunch of canvas items and so it does some integer
computation, but somehow integers are getting converted into rationals
and then being simplified which appears to be taking up heaps of time.

Has anyone got any hints on how to keep rationals out of my program? I
want to stick to integers and floats.

regards,

Richard.
[snip profiler output]
   
well, just remove the line
require 'rational'
from your program.

I wasn't including 'rational'.

I have since discovered that rationals come from the 'mathn' library and that library is being included by one of the libraries that I'm using. I also found that x.div(y) produces an integer while x/y produces a rational, so the answer is to use x.div(y) instead of x/y.

It is kind of freaky to have definition of x/y change dependent on whether or not a library is included. One library changing the semantics of the operations defined in another library is side effects on steroids.

irb(main):001:0> 1.div(2)
=> 0
irb(main):002:0> 1/2
=> 0
irb(main):003:0> require 'mathn'
=> true
irb(main):004:0> 1/2
=> 1/2
irb(main):005:0> 1.div(2)
=> 0

So there are three safe combinations (mathn,/), (mathn,div) (none,div) and one unsafe combination (none,/). It is a pitty that the unsafe combination is the first one that a nieve programmer would go for. Maybe this can be cleaned up in the Ruby 2.0?

regards,

Richard.

···

On 29/04/05, Richard Cole <rcole@itee.uq.edu.au> wrote:

Richard Cole ha scritto:

It is kind of freaky to have definition of x/y change dependent on whether or not a library is included. One library changing the semantics of the operations defined in another library is side effects on steroids.

yes, and we love that :slight_smile:
The point of using rational (or complex or mathn or whatever) is that you're slightly changing the language, so for general purpose you don't need to support the whole world of chances but you can add them when you really need them.

irb(main):001:0> 1.div(2)
=> 0
irb(main):002:0> 1/2
=> 0
irb(main):003:0> require 'mathn'
=> true
irb(main):004:0> 1/2
=> 1/2
irb(main):005:0> 1.div(2)
=> 0

So there are three safe combinations (mathn,/), (mathn,div) (none,div) and one unsafe combination (none,/). It is a pitty that the unsafe combination is the first one that a nieve programmer would go for. Maybe this can be cleaned up in the Ruby 2.0?

see the rcr "make rational a builtin class"
http://rcrchive.net/rcr/show/260

[...]

I have since discovered that rationals come from the 'mathn' library and
that library is being included by one of the libraries that I'm using. I
also found that x.div(y) produces an integer while x/y produces a
rational, so the answer is to use x.div(y) instead of x/y.

It is kind of freaky to have definition of x/y change dependent on
whether or not a library is included. One library changing the semantics
of the operations defined in another library is side effects on steroids.

[...]

So there are three safe combinations (mathn,/), (mathn,div) (none,div)
and one unsafe combination (none,/). It is a pitty that the unsafe
combination is the first one that a nieve programmer would go for. Maybe
this can be cleaned up in the Ruby 2.0?

Ruby 2.0 will do something with "selector namespaces" that may help here.

See Captcha

The effect would be that the library you're using would be able to use
Rationals in the current way, but your own code would not be affected.

···

In article <4271B929.6040900@itee.uq.edu.au>, Richard Cole wrote:

[...]

Problem is though, the original poster didn't do require 'rational'. They
just loaded a library that happened to use rational.

What if you want to use two libraries, one of which requires a/b to be
rational, and the other requires a/b to be integer division?

Selector namespaces are exciting :slight_smile:

···

In article <4hmce.70512$IN.1220555@twister2.libero.it>, gabriele renzi wrote:

Richard Cole ha scritto:

It is kind of freaky to have definition of x/y change dependent on
whether or not a library is included. One library changing the semantics
of the operations defined in another library is side effects on steroids.

yes, and we love that :slight_smile:
The point of using rational (or complex or mathn or whatever) is that
you're slightly changing the language, so for general purpose you don't
need to support the whole world of chances but you can add them when you
really need them.