Ruby Binding (FFI)

Purpose

The official ruby bindings from the 0mq project only work with ruby runtimes that support the full C extension API. At this time that pretty much limits that gem to MRI 1.8.x and 1.9.x. The JRuby and Rubinius projects are fleshing out their C extension support so the official zmq gem may run on those platforms now.

This gem adds FFI bindings so that any ruby runtime supporting the latest FFI API will work. At this time the minimum versions that will work with the FFI bindings are MRI 1.8.7-p249, MRI 1.9.1-p376, JRuby 1.5 and Rubinius (commit df9f7467).

Performance Notes

Using Ruby-FFI adds a little bit of overhead to C function calls since the binding itself is written in ruby with some C or Java helpers behind the scenes. I have made every effort to reduce this overhead to a minimum.

For example, the send and recv method calls return a Message class**. This class contains an FFI::Struct which mimics the zmq_msg_t struct. It provides methods for returning an FFI::MemoryPointer pointing to the data buffer as well as a call to return the size of that buffer. Due to this wrapping, no copying of the zmq_msg_t is performed.

To process the data buffer, I recommend subclassing Message and lazily copy data from the buffer as necessary. Reference the FFI Wiki for information on how to work with FFI::MemoryPointer and FFI::Struct.

I ran a battery of tests on my desktop Mac. Your timings will definitely vary but the relative performance of each runtime should be roughly the same.

The latency test was performed on a lightly loaded machine using two shell terminals. Note that I used the "zerocopy" variant of the latency test; for the FFI bindings this means the message is not copied before being echoed back. This is an important note because the zmq gem does not have a similar mode. It turns every message into a Ruby string so it's doing more work.

Doing 10 million rounds gave the most stable results. Any shorter and the jitter was above 5 usec; doing more than 10 million didn't reduce the jitter.

% ruby remote_lat_zerocopy.rb tcp://127.0.0.1:5500 1024 10_000_000

and
% ruby local_lat_zerocopy.rb tcp://127.0.0.1:5500 1024 10_000_000

The average of 5 runs is posted below. This is an apples-to-oranges test.

Language Runtime Bindings Mean usecs Message Management
C C 88 manual
MRI 1.9.2-p0 zmq 2.0.7.1 N/A manual
MRI 1.9.2-p0 ffi-rzmq 0.6.0 96 manual
JRuby 1.5.1 —server ffi-rzmq 0.6.0 95 manual
Rubinius 1.0.1 df9f7467 ffi-rzmq 0.6.0 91 manual

Doing an apples-to-apples test (where the FFI bindings copy the message string) results in higher numbers.

Language Runtime Bindings Mean usecs Message Management
C C N/A manual
MRI 1.9.2-p0 zmq 2.0.7.1 102 GC
MRI 1.9.2-p0 ffi-rzmq 0.6.0 125 GC
JRuby 1.5.1 —server ffi-rzmq 0.6.0 120 GC
Rubinius 1.0.1 df9f7467 zmq 2.0.7.1 107 GC
Rubinius 1.0.1 df9f7467 ffi-rzmq 0.6.0 109 GC
** Returning a {{Message}} is different from the 0mq ruby 
bindings which take and return strings. Use ZMQ::Socket#send_string 
and ZMQ::Socket#recv_string to get the old behavior.

I don't think any earth-shattering conclusion can be drawn from these results. I did note that Rubinius consistently showed the lowest FFI overhead of any runtime. Also, MRI using the C extension is hard to beat on a fair test.

Source Code

http://github.com/chuckremes/ffi-rzmq

Build and installation

Install the latest ØMQ release from here:

http://www.zeromq.org/area:download

There are two options for installing the gem.

1. Install from rubygems.org via gem install.

% gem install ffi-rzmq

2. Install the latest master source from github.

$ git clone http://github.com/chuckremes/ffi-rzmq.git
$ cd ffi-rzmq
$ gem build ffi-rzmq.gemspec
$ gem install ffi-rzmq-*.gem

You may have to run the last command with sudo if you do not have permission to install to a protected system directory.

Windows

The steps for installing the gem are exactly the same. Building and installing the 0mq library may take a few extra steps. Be sure to reference the instructions on the 0mq download page.

Documentation

The gem includes some very basic rdoc. While the docs are rather sparse at the moment, be sure to check the examples gem directory. Also, the 0mq C API documentation is a good reference. These ruby bindings mimic that API exactly though a few extra sugarings have been tossed in.

Also, be sure to read the ZeroMQ User Guide. The text is growing almost daily with new examples and clearer explanations.

Learn by Example

The learn ruby ZeroMQ project uses ffi-rzmq , providing what is currently the largest set of examples using the ffi-rzmq gem.
http://github.com/andrewvc/learn-ruby-zeromq

Test Suite

Run the examples. Requires rspec, rake and bones to be installed.

% rake spec

Bug Reporting

If you encounter problems please fill a bug report at:

http://github.com/chuckremes/ffi-rzmq/issues

or write an e-mail to:

cremes AT mac DOT com

Mailing List

Discussions about this language binding take place on the general zeromq-dev list.

Page tags: ffi ruby