Calling D from Ruby
by Paweł Świątkowski
We all know that Ruby is slow. This is most common (and most missing-the-point, in fact) argument other-languages people fire against Ruby. Raw language performance is usually not so important when most of the time you call external resources, such as databases, filesystem or network, and wait for them. There are times, however, when you do things that require better computational speed or memory management than Ruby can offer.
Since Ruby is written in C, it is a common practice to write C extensions. C is, however, not the most pleasant to use language in the world, especially compared to Ruby. Therefore we might want to turn to other languages that generate C-compatible code. One of them is D, which aims to achieve what C++ failed to do - create fast, low level, object-oriented C-next-gen. Long time ago Tomasz Stachewicz tried to write a system allowing to write Ruby extensions in D. This, however, was abandoned long time ago. Other people claim that they managed to run Ruby with D in both directions, but these are with version 1.9.3, so not really interesting. We can, however, run D code with help of Ruby FFI. This is how.
The example I chose was finding first prime number greater than given input. This might not be the wisest choice or most useful problem, but it consists of fair amount of calculations, so it will do to show the benefits. My “solution” is based on similar one for Rust.
The D code:
Note that you have to wrap your publicly exposed functions with
extern(C), but that’s about it. Now we need to create a dynamic library that FFI could use. To do it I followed this guide from D website. You call this:
Of course you may have to change
/usr/lib to other path where your
libphobos2.so is. And
prime.d to proper name of your source file. Then you tell FFI to use this file.
And this is it – you should see the result. So, is it really worth it? Well, to check this out I created similar code in Ruby (it wasn’t that easy because of lack of proper
for loop, but I hope I avoided any unnecessary overhead in implementation):
The result is this:
So it seems that the D variant is almost 10 times faster for the data provided. I played a bit with input but this ratio seemed to be almost constant. I think this proved it was worth the effort. And even though writing similar code in C would not be such nightmare, D is really much more pleasant in more complex situations (it has GC, yay! it has classes, another yay!).
So even though we cannot get full duplex compatibility between Ruby and D (for now) (turns out you can, see followup post) It might be a subject to thought to write some computational-heavy parts of your Ruby application in faster and modern language (be it D or Rust).