P
Phil Tomson
In general I've always seen things speed up when I've writtten C
extensions. I usually don't rewrite all of the methods in a class, just
certain speed critical ones.
In this case I've got a Point class:
#point.rb
module ACO
class Point
attr_accessor :x, :y
def initialize(x,y)
@x = x
@y = y
end
#calculate the distance between this point and other point
def -(other)
Math.sqrt((@x-other.x)**2+(@y-other.y)**2)
end
def ==(other)
return (@x == other.x) && (@y == other.y)
end
def to_s
"(#{@x},#{@y})"
end
end
end #ACO
if $0 == __FILE__
start_time = Time.now
100000.times do |i|
p1 = ACO:oint.new(rand(50),rand(50))
p2 = ACO:oint.new(rand(50),rand(50))
p1 == p2
p2 == p1
p1-p2
p2-p1
end
end_time = Time.now
puts "Total time for pure ruby point test: #{end_time - start_time}"
require 'ext/ACO_Ext' #load up the extension bundle
start_time = Time.now
100000.times do |i|
p1 = ACO:oint.new(rand(50),rand(50))
p2 = ACO:oint.new(rand(50),rand(50))
p1 == p2
p2 == p1
p1-p2
p2-p1
end
end_time = Time.now
puts "Total time for extension point test: #{end_time - start_time}"
end #point.rb
Here's the C code for the extension:
//aco_ext.c
#include "ruby.h"
#include <math.h>
#include <stdio.h>
static int id_x;
static int id_y;
static int id_equal;
static VALUE aco_point_diff(VALUE self, VALUE other){
double self_x = NUM2DBL(rb_iv_get(self, "@x"));
double self_y = NUM2DBL(rb_iv_get(self, "@y"));
double other_x= NUM2DBL(rb_iv_get(other, "@x"));
double other_y= NUM2DBL(rb_iv_get(other, "@y"));
double xdiffsq= pow( (self_x - other_x),2 );
double ydiffsq= pow( (self_y - other_y),2 );
return rb_float_new(sqrt( xdiffsq + ydiffsq ) );
}
static VALUE aco_point_init(VALUE self, VALUE x, VALUE y){
rb_iv_set(self,"@x",x);
rb_iv_set(self,"@y",y);
return self;
}
static VALUE aco_point_equal(VALUE self, VALUE other){
double self_x = NUM2DBL(rb_iv_get(self, "@x"));
double self_y = NUM2DBL(rb_iv_get(self, "@y"));
double other_x= NUM2DBL(rb_iv_get(other, "@x"));
double other_y= NUM2DBL(rb_iv_get(other, "@y"));
return ((self_x == other_x) && (self_y == other_y));
}
VALUE cACOMod;
VALUE cACOPoint;
VALUE cACOGraph;
void Init_ACO_Ext() {
printf("ACO_Ext initializing...\n");
cACOMod = rb_define_module("ACO");
cACOPoint = rb_define_class_under(cACOMod,"Point",rb_cObject);
cACOGraph = rb_define_class_under(cACOMod,"Graph",rb_cObject);
rb_define_method(cACOPoint,"initialize",aco_point_init,2);
rb_define_method(cACOPoint,"-",aco_point_diff,1);
rb_define_method(cACOPoint,"==",aco_point_equal,1);
id_x = rb_intern("x");
id_y = rb_intern("y");
id_equal = rb_intern("==");
}
//end of aco_ext.c
And the results:
l% ruby point.rb
Total time for pure ruby point test: 8.600752
ACO_Ext initializing...
Total time for extension point test: 6.086567
Using the extension it's 2.6 second _slower_ than the pure Ruby
implementation!?
Phil
extensions. I usually don't rewrite all of the methods in a class, just
certain speed critical ones.
In this case I've got a Point class:
#point.rb
module ACO
class Point
attr_accessor :x, :y
def initialize(x,y)
@x = x
@y = y
end
#calculate the distance between this point and other point
def -(other)
Math.sqrt((@x-other.x)**2+(@y-other.y)**2)
end
def ==(other)
return (@x == other.x) && (@y == other.y)
end
def to_s
"(#{@x},#{@y})"
end
end
end #ACO
if $0 == __FILE__
start_time = Time.now
100000.times do |i|
p1 = ACO:oint.new(rand(50),rand(50))
p2 = ACO:oint.new(rand(50),rand(50))
p1 == p2
p2 == p1
p1-p2
p2-p1
end
end_time = Time.now
puts "Total time for pure ruby point test: #{end_time - start_time}"
require 'ext/ACO_Ext' #load up the extension bundle
start_time = Time.now
100000.times do |i|
p1 = ACO:oint.new(rand(50),rand(50))
p2 = ACO:oint.new(rand(50),rand(50))
p1 == p2
p2 == p1
p1-p2
p2-p1
end
end_time = Time.now
puts "Total time for extension point test: #{end_time - start_time}"
end #point.rb
Here's the C code for the extension:
//aco_ext.c
#include "ruby.h"
#include <math.h>
#include <stdio.h>
static int id_x;
static int id_y;
static int id_equal;
static VALUE aco_point_diff(VALUE self, VALUE other){
double self_x = NUM2DBL(rb_iv_get(self, "@x"));
double self_y = NUM2DBL(rb_iv_get(self, "@y"));
double other_x= NUM2DBL(rb_iv_get(other, "@x"));
double other_y= NUM2DBL(rb_iv_get(other, "@y"));
double xdiffsq= pow( (self_x - other_x),2 );
double ydiffsq= pow( (self_y - other_y),2 );
return rb_float_new(sqrt( xdiffsq + ydiffsq ) );
}
static VALUE aco_point_init(VALUE self, VALUE x, VALUE y){
rb_iv_set(self,"@x",x);
rb_iv_set(self,"@y",y);
return self;
}
static VALUE aco_point_equal(VALUE self, VALUE other){
double self_x = NUM2DBL(rb_iv_get(self, "@x"));
double self_y = NUM2DBL(rb_iv_get(self, "@y"));
double other_x= NUM2DBL(rb_iv_get(other, "@x"));
double other_y= NUM2DBL(rb_iv_get(other, "@y"));
return ((self_x == other_x) && (self_y == other_y));
}
VALUE cACOMod;
VALUE cACOPoint;
VALUE cACOGraph;
void Init_ACO_Ext() {
printf("ACO_Ext initializing...\n");
cACOMod = rb_define_module("ACO");
cACOPoint = rb_define_class_under(cACOMod,"Point",rb_cObject);
cACOGraph = rb_define_class_under(cACOMod,"Graph",rb_cObject);
rb_define_method(cACOPoint,"initialize",aco_point_init,2);
rb_define_method(cACOPoint,"-",aco_point_diff,1);
rb_define_method(cACOPoint,"==",aco_point_equal,1);
id_x = rb_intern("x");
id_y = rb_intern("y");
id_equal = rb_intern("==");
}
//end of aco_ext.c
And the results:
l% ruby point.rb
Total time for pure ruby point test: 8.600752
ACO_Ext initializing...
Total time for extension point test: 6.086567
Using the extension it's 2.6 second _slower_ than the pure Ruby
implementation!?
Phil