Image for post
Image for post

Rust to the rescue (of Ruby)

Fabiano B.
Oct 13, 2015 · 5 min read

You are working with Ruby, probably with Rails or Sinatra. Your company has an algorithm that is becoming more complex and important everyday.

Since the user needs to see the result of this algorithm, the requirement is that it must run blazingly fast and (of course) doesn’t crash.

For the sake of this article, let’s simplify and say that your algorithm uses fibonacci of 42 aka F(42) inside to calculate the result and this is the heaviest computation.

DISCLAIMER: Fibonacci is not the point of this article at all. The goal is to illustrate how Rust can be combined with Ruby when performance is the goal. Probably standard deviation is a better example but I could not make

The Algo in Ruby

Let’s implement Fibonnaci in Ruby. It is very simple to do so:

Now let’s ask it to calculate F(42). (If you want to run this on your machine, this is the time to have some coffee)

It takes 43.38 seconds!

It is very slow to use it inside a Web application for real time calculation.

This doesn’t fit our first requirement, speed. It must run faster! How can we do that?

We could make some improvements in the Ruby code, like memoization that would solve the problem. But this is not the point of this article. Let’s try Rust.

Rust for the rescue

Let’s give Rust a try and see how much it can improve the performance of our algorithm.

Rust comes with Cargo, that is a very handy CLI. To start the project, run:

That command creates this structure:

Cargo.toml is like a mix of gemspec with Gemfile. For now it contains these lines:

Open up src/lib.rs, it comes with this:

The #[test] is an annotation saying that this will be executed in the test suite, so let the TDD begin!

TDD

Starting simple, F(1) should be 1.

We can run the test with:

It fails to compile because we did not define the fibonacci function in Rust yet.

This is a simple function in Rust. The function declaration says that it receives a parameter named n that is a 32-bit signed integer type (i32) and responds an integer as well.

For now it is responding hard-coded 1, that is enough to make the test pass. Let’s check it:

And this time:

Yay! It passes!

Let’s go ahead and add some more scenarios to our test suite:

With this we expect some failing tests, and to make them pass:

Run the test suite again:

All green! :)

Now that we’ve got the Rust version, we need to connect it with Ruby.

Connecting Ruby with Rust

PS: This part of the article is very inspired in this chapter of the Rust book.

In order to accomplish this, we’re going to use FFI (foreign function interface).

But before that we need some small adjustments to use it as an extension. Edit the src/lib.rs to be like this:

So basically we need to add the #[no_mangle] on the top of the function and pub extern in the beginning of the function declaration.

These changes are required to tell the Rust compiler to preserve the name of the function and it is going to be called from outside.

Add this to Cargo.toml:

This tells Rust to compile it as a dynamic library and not as Rust format.

Let’s build it now:

This will generate some files inside target/release:

We are interested in libfibonacci.dylib (depending on your OS this file may have another extension).

To call this library from Ruby we need the FFI gem. Install it with:

Place a fibo.rb at the root of your project and add this code:

This a simple Ruby module that

  • extends FFI
  • Points to the library we’ve generated with Rust.
  • attaches the Fibonnaci function from Rust to Ruby. The “[:int], :int” are the parameters it receives and what it responds, respectively.

That’s it! We can run it with:

It responds 267914296 as expected. It works, yay!

Benchmark

Let’s check the performance improvement:

Image for post
Image for post

The Rust code finishes in 0.91 seconds. Less than a second.

Rust is 4767.03% faster than Ruby in this example.

Conclusion

Of course that Rust would be faster than Ruby. This is not a language comparison.

For your user, a fast feedback matters a lot and Rust can be a good friend for Ruby when performance is the goal :)

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store