Ruby-inline and alias_method

Hi,

I have be experimenting with Ruby-Inline, and I have to say it is
super cool. I have run into a problem that is either a problem in my
code (likely), or a problem with inline, or perhaps a problem with
ruby. The problem is that when I access an inlined method that has
been aliased, it slows down 100 fold. below is an example that just
sums the elements of an array. I also run it without using the alias
and it runs fast (about 9 times as fast as ruby native). I also
include at the bottom the code that Ruby-Inline generated.

#!/usr/local/bin/ruby
require ‘RubyInline-1.0.5/inline’

class Array
include Inline

def sum(*args)
inline args, <<-'END’
int len, result = 0;
int j;
VALUE *arr;

arr = RARRAY(self)->ptr;
len = RARRAY(self)->len;
for(j=0; j<len; j++) {
    result += FIX2INT(arr[j]);
}
return INT2FIX(result);
END

end

def slowsum
  result = 0
  each { |x| result += x }
  result;
end

end

iters = 100000
methods = [‘slowsum’,‘sum’]

methods.each do |meth|
Array.send(:alias_method, :thesum, meth.intern)

tstart = Time.now
iters.times do
  [0,1,2,3,4,5,6,7,8,9,10].thesum
end
tend = Time.now
total = tend - tstart
avg = total / iters

printf "Type = #{meth}, Iter = #{iters}, time = %.8f sec, %.8f sec / iter\n", total, avg

end

now check this without the aliased version

tstart = Time.now
iters.times do
[0,1,2,3,4,5,6,7,8,9,10].sum
end
tend = Time.now
total = tend - tstart
avg = total / iters

printf “Type = normal sum (no alias), Iter = #{iters}, time = %.8f sec, %.8f sec / iter\n”, total, avg

exit

···

#--------------
The code that was generated by inline is:

#include “ruby.h”

static VALUE t_sum(int argc, VALUE *argv, VALUE self) {
int len, result = 0;
int j;
VALUE *arr;

arr = RARRAY(self)->ptr;
len = RARRAY(self)->len;
for(j=0; j<len; j++) {
    result += FIX2INT(arr[j]);
}
return INT2FIX(result);

}

VALUE cMod_Array_sum;

void Init_Mod_Array_sum() {
cMod_Array_sum = rb_define_module(“Mod_Array_sum”);
rb_define_method(cMod_Array_sum, “_sum”, t_sum, -1);
}

#--------------
The output of the program was:

Type = slowsum, Iter = 100000, time = 1.36584400 sec, 0.00001366 sec / iter
Type = sum, Iter = 100000, time = 16.48371500 sec, 0.00016484 sec / iter
Type = normal sum (no alias), Iter = 100000, time = 0.14434800 sec, 0.00000144 sec / iter

thanks,
-joe

I have be experimenting with Ruby-Inline, and I have to say it is
super cool. I have run into a problem that is either a problem in my
code (likely), or a problem with inline, or perhaps a problem with
ruby. The problem is that when I access an inlined method that has
been aliased, it slows down 100 fold. below is an example that just
sums the elements of an array. I also run it without using the alias
and it runs fast (about 9 times as fast as ruby native). I also
include at the bottom the code that Ruby-Inline generated.

Matz or other extern/ruby gurus:

I’m reasonably sure that it’s not RubyInline that is causing this
problem. I’m still not sure what is, but it seems to be centered around
the following line:

    Array.send(:alias_method, :thesum, meth.intern)

If he runs the benchmark w/o this madness, then the inlined C code is
9x faster than the ruby code.

···

On Thursday, September 19, 2002, at 05:54 PM, Joseph McDonald wrote:

#!/usr/local/bin/ruby
require ‘RubyInline-1.0.5/inline’

class Array
include Inline

def sum(*args)
inline args, <<-'END’
int len, result = 0;
int j;
VALUE *arr;

arr = RARRAY(self)->ptr;
len = RARRAY(self)->len;
for(j=0; j<len; j++) {
    result += FIX2INT(arr[j]);
}
return INT2FIX(result);
END

end

def slowsum
  result = 0
  each { |x| result += x }
  result;
end

end

iters = 100000
methods = [‘slowsum’,‘sum’]

methods.each do |meth|
Array.send(:alias_method, :thesum, meth.intern)

tstart = Time.now
iters.times do
  [0,1,2,3,4,5,6,7,8,9,10].thesum
end
tend = Time.now
total = tend - tstart
avg = total / iters

printf "Type = #{meth}, Iter = #{iters}, time = %.8f sec, %.8f sec 

/ iter\n", total, avg
end

now check this without the aliased version

tstart = Time.now
iters.times do
[0,1,2,3,4,5,6,7,8,9,10].sum
end
tend = Time.now
total = tend - tstart
avg = total / iters

printf “Type = normal sum (no alias), Iter = #{iters}, time = %.8f
sec, %.8f sec / iter\n”, total, avg

exit

#--------------
The code that was generated by inline is:

#include “ruby.h”

static VALUE t_sum(int argc, VALUE *argv, VALUE self) {
int len, result = 0;
int j;
VALUE *arr;

arr = RARRAY(self)->ptr;
len = RARRAY(self)->len;
for(j=0; j<len; j++) {
    result += FIX2INT(arr[j]);
}
return INT2FIX(result);

}

VALUE cMod_Array_sum;

void Init_Mod_Array_sum() {
cMod_Array_sum = rb_define_module(“Mod_Array_sum”);
rb_define_method(cMod_Array_sum, “_sum”, t_sum, -1);
}

#--------------
The output of the program was:

Type = slowsum, Iter = 100000, time = 1.36584400 sec, 0.00001366 sec /
iter
Type = sum, Iter = 100000, time = 16.48371500 sec, 0.00016484 sec /
iter
Type = normal sum (no alias), Iter = 100000, time = 0.14434800 sec,
0.00000144 sec / iter

thanks,
-joe

I’m reasonably sure that it’s not RubyInline that is causing this
problem. I’m still not sure what is, but it seems to be centered
around the following line:
Array.send(:alias_method, :thesum, meth.intern)

OK… I have a fix on the problem, but not a fix FOR the problem… It
is definately caused by the above alias_method. Essentially, he aliases
BEFORE the inline method is called, see below:

methods.each do |meth|
Array.send(:alias_method, :thesum, meth.intern)

...
  [0,1,2,3,4,5,6,7,8,9,10].thesum
...

so, :thesum winds up pointing to the original method :sum. But :sum is
the method that calls inline, and inline runs the following:

 myclass.class_eval("include #{mod_name}")
 myclass.class_eval("alias_method :old_#{mymethod}, :#{mymethod}")
 myclass.class_eval("alias_method :#{mymethod}, :_#{mymethod}")

which move :sum to :old_sum, and :_sum (the extern version) to :sum.

His code then calls :thesum, which is a direct alias (ie reference) to
(now) :old_sum which will call the dynamic loader over and over. In
other words, he always runs the slowest possible version.

The first answer is “don’t alias a method that calls inline”, but is
that reasonable? This would be where smalltalk’s #becomes: method would
really shine. All references to the old method would end up pointing to
the new method… but #becomes: is hard to implement and dangerous
(must shut down GC, manually walk object graph, etc). I’d rather ruby
keep compiling and linking in 3 minutes on my system rather than be
able to handle everything…

Is there a better way for me to handle this? Can I at all? Maybe some
object_space madness? I don’t think this is possible because I’d need
access to, and the ability to change, every reference, not every object.

···

On Thursday, September 19, 2002, at 06:05 PM, Ryan Davis wrote: