Ruby missing constant expression optimization?

I expect the Ruby analyzer to do this trivial optimization, but it doesn't seem to be the case (we are talking about implementations of YARV, Ruby 1.9.x, 2.0.0):

require 'benchmark' def fib1 a, b = 0, 1 while b < 9999**4000 a, b = b, a+b end puts "\tdone !" end MAX_FIB = 9999**4000 def fib2 a, b = 0, 1 while b < MAX_FIB a, b = b, a+b end puts "\tdone !" end if __FILE__ == $0 Benchmark.bm do |r| r.report('plain') { fib1 } r.report('predefined constant') { fib2 } end end plain done ! 32.810000 0.220000 33.030000 ( 33.069854) predefined constant done ! 0.120000 0.000000 0.120000 ( 0.119303) 

Since both methods are identical, except for using a predefined constant instead of a constant expression in the second, it seems that the Ruby interpreter calculates the constant power of each cycle again and again.

Is there any material why Ruby does not do this basic optimization at all or only in some specific cases?

+6
source share
2 answers

I'm sorry to give another answer, but I do not want to delete or edit my previous one due to the interesting discussion under it.

As Jörg W Mittag said, a ruby ​​has to compute the expression given by while each time, because it is simply impossible to say that it is constant. For an extreme example, I came up with the following code:

 class Fixnum alias :original_plus :+ @@increment = 0 def self.increase! @@increment = @@increment.original_plus 1 end def +(other) (original_plus(other)).original_plus(@@increment) end end while 1+1 < 5 puts Fixnum.increase! end puts 'done' # 1 # 2 # 3 # done 
+2
source

Try:

 def fib1 a, b = 0, 1 while b < 9999**4000 a, b = b, a+b end puts "\tdone !" end def fib2 a, b = 0, 1 value = 9999**4000 while b < value a, b = b, a+b end puts "\tdone !" end 

There will be no difference!

The reason is that in your first test, ruby ​​computes 9999**4000 inside the method you are calling, while in the second test you use a constant that is calculated outside (before running Benchmark), and therefore your second result shorter for '9999 ** 4000' calculation time.

UPDATE: Well, there really is a huge difference between the two tests, and the ruby ​​calculates 9999 ** 4000 in timelessness - see @JorgWMittag's comments below.

I do not delete this answer to save comments.

0
source

All Articles