As you might already know, the Ruby interpreter you’re (probably) using (the one at ruby-lang.org) know as MRI, for Matz Ruby Interpreter, is written in C. The VM is entirely written in C and most of the standard libraries too. So it seems that the way to make code run faster is really to dive into C.
Now I hear you yell with your ears all red, eyes full of blood and a big vein pumping on your forehead: But it will be unmaintainable!
If everything seems under control, you’re not going fast enough
- Mario Andretti
The hard part is that the C API of MRI lacks documentation. You either have to search through the mailinglist, look at other people’s code or try to guess from Ruby’s code. I recently ported a bit of code from Ruby to C and here are a couple of Ruby to C mapping that might help you.
If you’ve never written or looked at a Ruby extension before, I suggestion reading Peter Cooper excellent and very simple tutorial on how to set things up.
Objects are VALUEs
|
|
Strings
|
|
Object to C struct
In C you'll probably want to store your data in a struct. Ruby provide some things to wrap a struct inside a Ruby object. Also, since Ruby is not aware of the stuff created in the C world we have to be pretty explicit about everything.
module MyModule class MyClass def close @closed = true end end endIn C
/* This is called by Ruby GC when the object is freed * so free all resources used here. We hook it up * in the Data_Wrap_Struct call down there. */ void my_obj_free(my_struct_t *s) { free(s); } /* Called by Ruby then and instance of your class is created * hooked by rb_define_alloc_func. */ VALUE my_obj_alloc(VALUE klass) { my_struct_t *s = ALLOC_N(my_struct_t, 1); /* This stores the struct inside the Ruby object, so you * can get the struct back on each method call. */ return Data_Wrap_Struct(klass, NULL, my_obj_free, s); } /* The actual MyClass#close method, first argument is the * instance object. It's hook into our class by the rb_define_method * call in Init_ */ VALUE my_obj_close(VALUE self) { my_struct_t *s; /* This is where we get the struct back from the Ruby object */ Data_Get_Struct(self, my_struct_t, s); s->closed = 1; } /* Init_* is magically called by Ruby to bootstrap your extension, * it's like a main function if you will. */ void Init_name_of_your_extension() { VALUE mMyModule = rb_define_module("MyModule"); VALUE cMyClass = rb_define_class_under(mMyModule, "MyClass", rb_cObject); rb_define_alloc_func(cMyClass, my_obj_alloc); rb_define_method(cMyClass, "close", my_obj_close, 0); }Calling methods
|
|
Blocks
|
|
Error handling
|
|
Garbage Collection
When creating Ruby objects in the C world, Ruby is not aware of the scope of each, it's impossible to track when a variable is used and when it's not, so it's impossible for the garbage collector to know when do leave or sweep objects. If you create a long live object, that will persist for several method calls you have to tell Ruby's garbage collector when it's in use and when you're finished with it. If you don't, Ruby GC will free your variable in the worst possible time, ending up with unthinkable tragedies around the world, leaving you homeless and poor for all eternity.
VALUE var = rb_str_new2("prout"); /* Tell Ruby GC this object is in use */ rb_gc_register_address(&var); /* when you're finished you can unregister it and let the GC free it */ rb_gc_unregister_address(&var);Note that you don't need to do this if you only use the variable in one method since the control is not given back to Ruby during the method call. Also if the variable is still referenced in the Ruby world it's already registered.
More, more, more!
This is far from a complete guide to MRI, you'll need to dig the Ruby doxygen doc if you need more info.
Now lets port Rails to C!


Keep everything simple, as simple as it can get. Don’t try to solve all the problems in the world all at once. Find one problem, for example: greasy door handles, and solve that single problem: a web site for locating clean door handles in a specific building.
It’s like a 3 legs chicken, if you pull on the features leg, you’re gonna need more resources and time. But if your time and resources are restricted, your only option is to cut off the features leg (who wants a 3 legs chicken anyway?).
Making 1000 lines of code perfect in a respectable amount of time is impossible. But tuning 10 to perfection is easy and fun. It’s also easier to wrap your head around the tiny problem you’re trying to fix.
Incremental Perfectionism, is like the poor-artist-on-crack perfectionism on a smaller, healthier scale. Don’t try to make the whole thing perfect. But never ever settle for something that is not the best possible solution to the small problem you’re solving. Don’t copy paste some code because it’s easier, don’t comment out a block of code because it’s faster, don’t live with ugly code. But don’t fix it all at the same time!
If you feel you’re breaking any of the previous rules, start over. Maybe your changeset is getting too big. You’re trying to change too much things at the same time. If you’re afraid to loose your precious code, commit your working copy to a branch:
A good way to trigger your imagination into moving your software closer to perfection is too look at how other people are trying to do it. Open source projects are open books into some of the best programmers mind. They’ve spent time trying several ways to solve a problem, don’t spent your time doing the same thing!







