CSC/ECE 517 Fall 2010/ch3 3i MM: Difference between revisions

From Expertiza_Wiki
Jump to navigation Jump to search
Line 80: Line 80:
     end
     end
end
end
</pre>
Results:
<pre>
$ ruby perf_test.rb
  C Test: 1.192747
  C Result: 3.14159491666667
  Ruby Test: 264.017448
  Ruby Result: 3.14159491666667
</pre>
</pre>



Revision as of 06:59, 7 October 2010

Introduction

Ruby is a high-level, dynamic programming language that is useful for everything from short scripting assignments, to entire web applications and desktop GUI applications. However, there are inherent downsides to using Ruby come because of its interpreted, dynamic nature. Using C code for some tasks can improve memory usage, and raw execution speed when compared to Ruby [1].

However, what if you could combine the good performance aspects of C, with with the dynamic elements of Ruby? It seems like it should be possible, since Ruby is actually written in C. Well, luckily the maintainers of Ruby have thought ahead, and have made that possible in several different ways. There ways to extend Ruby's capabilities with C code, and there ways to use Ruby functions within your own C application. This page will attempt to cover aspects of both of these mechanisms.

Using C from Ruby

Following are some overviews and examples of ways to create C or C++ extensions to Ruby.

Ruby C API

README.EXT

The README.EXT file contains the latest information and an overview of how to create Ruby extensions in C. It is an invaluable source of information, and is included in any source code distribution of Ruby. The link is to the latest HEAD version in Ruby's official subversion repository, but you may want to read the version that came with your installed version of Ruby.

In Unix-like distributions, for example, this file may be installed installed in '/usr/share/doc/ruby1.8-dev/README.EXT.gz'. To read, from a command prompt type:

> zless /usr/share/doc/ruby1.8-dev/README.EXT.gz

VALUE

In C, every variable has a type. In Ruby, everything is an object. To bridge the gap between C's static types and Ruby's dynamic objects, Ruby creators came up with the "VALUE" typedef in C. In the Ruby C API, when a variable is of type "VALUE, you know that it either comes from the Ruby side of the program, will be returned to Ruby, or will be used by the Ruby side in some form or fashion [2]. The typedef VALUE is defined in ruby.h, and is typically an unsigned long. The Ruby interpreter uses VALUE as either a pointer to a larger Ruby data type, or - in the case of more primitive types, such as Fixnums, booleans, and the NilClass - to actually the value itself.

Example

Performance Comparison

This is a short test to compare an iterative calculation of PI in both a C function, and Ruby. The test yielded quite surprising results. This is a relatively inefficient way to calculate PI, but it works, nonetheless.

C file with function

#include "ruby.h"
VALUE PiCalc_C = Qnil;

static VALUE pi_calc_c(VALUE self)
{
    int numPartitions = 12000;
    int circleCount = 0;
    double interval = 0, pi = 0;
    int i = 0, j = 0;
    double a, b;

    interval = 1.0/(double)numPartitions;

    for (i = 0; i < numPartitions; i++) {
        a = (i + .5)*interval;
        printf("A: %f\n", a);
        for (j = 0; j < numPartitions; j++) {
            b = (j + .5)*interval;
            if ((a*a + b*b) <= 1) circleCount++;
        }
    }

    pi = (double)(4*circleCount)/(numPartitions * numPartitions);
    return LONG2NUM(pi);
}


// The initialization method for this module
void Init_pi_calc_c() {
    PiCalc_C = rb_define_module("PiCalc_C");
    rb_define_method(PiCalc_C, "pi_calc_c", pi_calc_c, 0);
}

Here is the Ruby function that does the exact same calculation:

module PiCalcRuby
    def pi_calc_ruby
        numPartitions = 12000
        circleCount = interval = pi = 0.0
        interval = 1.0/numPartitions;
        
        for i in 0..numPartitions do
            a = (i + 0.5)*interval
            puts a
            for j in 0..numPartitions do
                b = (j + 0.5)*interval
                if ((a*a + b*b) <= 1) then
                    circleCount += 1
                end
            end
        end
        pi = (4*circleCount)/(numPartitions * numPartitions)
    end
end


Results:

$ ruby perf_test.rb 
  C Test: 1.192747
  C Result: 3.14159491666667
  Ruby Test: 264.017448
  Ruby Result: 3.14159491666667

RubyInline

RubyInline is

Installation

RubyInline is a separate gem, available through rubygems, and can be installed thusly:

> gem install RubyInline

Example

require 'rubygems'
require 'inline'

class Example
    inline(:C) do |builder|
        builder.c "int method_test1() {
            int x = 10;
            return x;
        }"
    end
end

puts Example.new.method_test1

SWIG

SWIG is a popular wrapper generator that can produce wrappers for C and C++ code in several high level programming languages, including Perl, Python, and Ruby.

Using Ruby from C

README.EXT

Once again, this file, included in the Ruby source code, has a section on using Ruby features from C. Section 2.2 is a brief overview of some techniques for doing this.

Conclusion

References

External Links

RubyInline

Ruby C Extension in Under 5 Minutes

Ruby C Extension Overview