ffi -- creating cross engine rubygems

38
FFI MAKING CROSS ENGINE EXTENSIONS Jeremy Hinegardner [email protected] twitter: copiousfreetime 1 Tuesday, November 24, 2009

Upload: jeremy-hinegardner

Post on 15-May-2015

1.637 views

Category:

Technology


5 download

DESCRIPTION

FFI is a way to create cross engine rubygems. Wrap a library once, and use it from MRI, JRuby, Rubinious, and mingw32 Ruby. This talk will discuss FFI why you want to use it.

TRANSCRIPT

Page 1: FFI -- creating cross engine rubygems

FFIMAKING CROSS ENGINE EXTENSIONS

Jeremy [email protected]

twitter : copiousfreetime

1Tuesday, November 24, 2009

Page 2: FFI -- creating cross engine rubygems

I LOVE RUBY

2Tuesday, November 24, 2009

Page 3: FFI -- creating cross engine rubygems

I LIKE C

3Tuesday, November 24, 2009

Page 4: FFI -- creating cross engine rubygems

SURVEY TIME

4Tuesday, November 24, 2009

Page 5: FFI -- creating cross engine rubygems

RUBY DEVELOPERS?

5Tuesday, November 24, 2009

Page 6: FFI -- creating cross engine rubygems

WRITER OF LIBRARIES?

6Tuesday, November 24, 2009

Page 7: FFI -- creating cross engine rubygems

WRITER OF EXTENSIONS?

7Tuesday, November 24, 2009

Page 8: FFI -- creating cross engine rubygems

USED BY MULTIPLE ENGINES?

8Tuesday, November 24, 2009

Page 9: FFI -- creating cross engine rubygems

WHAT IS FFI?

9Tuesday, November 24, 2009

Page 10: FFI -- creating cross engine rubygems

LIBFFIhttp://sourceware.org/libffi/

“A foreign function interface is the popular name for the interface that allows code written in one language

to call code written in another language.”

10Tuesday, November 24, 2009

Page 11: FFI -- creating cross engine rubygems

FOR RUBYISTS

The Goal :

An extension based upon FFI should be compatible with many ruby

engines.

11Tuesday, November 24, 2009

Page 12: FFI -- creating cross engine rubygems

libfoo.so

12Tuesday, November 24, 2009

Page 13: FFI -- creating cross engine rubygems

libfoo.so

Applicationuses

libfoo_bar()

12Tuesday, November 24, 2009

Page 14: FFI -- creating cross engine rubygems

libfoo.so

Applicationuses

libfoo_bar()

FFI

12Tuesday, November 24, 2009

Page 15: FFI -- creating cross engine rubygems

libfoo.so

Applicationuses

libfoo_bar()

FFIRuntim

e

12Tuesday, November 24, 2009

Page 16: FFI -- creating cross engine rubygems

libfoo.so

ruby-foo C extension

Matz RubyInterpreter

App

13Tuesday, November 24, 2009

Page 17: FFI -- creating cross engine rubygems

libfoo.so

ruby-foo C extension

Matz RubyInterpreter

App

JRubyInterpreter

App

RubiniusInterpreter

App

13Tuesday, November 24, 2009

Page 18: FFI -- creating cross engine rubygems

libfoo.so

Matz RubyInterpreter

App

JRubyInterpreter

App

RubiniusInterpreter

App

13Tuesday, November 24, 2009

Page 19: FFI -- creating cross engine rubygems

libfoo.so

Matz RubyInterpreter

App

JRubyInterpreter

App

RubiniusInterpreter

App

13Tuesday, November 24, 2009

Page 20: FFI -- creating cross engine rubygems

libfoo.so

Matz RubyInterpreter

App

JRubyInterpreter

App

RubiniusInterpreter

App

built-in libffi or ffi gem

13Tuesday, November 24, 2009

Page 21: FFI -- creating cross engine rubygems

libfoo.so

Matz RubyInterpreter

App

JRubyInterpreter

App

RubiniusInterpreter

App

built-in libffi or ffi gem

foo-ffi (pure ruby)

13Tuesday, November 24, 2009

Page 22: FFI -- creating cross engine rubygems

LIBSIMPLE_METRICShttp://github.com/copiousfreetime/libsimple_metrics

14Tuesday, November 24, 2009

Page 23: FFI -- creating cross engine rubygems

 1 #ifndef __SIMPLE_METRICS_H__ 2 #define __SIMPLE_METRICS_H__ 3 #include <stdlib.h> 4 #include <math.h> 5  6 typedef struct _simple_metrics{ 7     double min; 8     double max; 9     double sum;10     double sumsq;11     long   count;12 } simple_metrics ;13 14 simple_metrics* simple_metrics_new();15 void            simple_metrics_free(   simple_metrics* sm );16 void            simple_metrics_update( simple_metrics* sm, double value );17 double          simple_metrics_mean(   simple_metrics* sm );18 double          simple_metrics_min(    simple_metrics* sm );19 double          simple_metrics_max(    simple_metrics* sm );20 double          simple_metrics_sum(    simple_metrics* sm );21 long            simple_metrics_count(  simple_metrics* sm );22 double          simple_metrics_stddev( simple_metrics* sm );23 double          simple_metrics_rate(   simple_metrics* sm );24 25 #endif

15Tuesday, November 24, 2009

Page 24: FFI -- creating cross engine rubygems

 1 class Metric 2   attr_reader :name 3   def initialize( name ) 4     @name 5   end 6  7   def update( new_value) ... end 8   def count  ... end 9   def max    ... end10   def mean   ... end11   def min    ... end12   def rate   ... end13   def sum    ... end14   def stddev ... end15 end

16Tuesday, November 24, 2009

Page 25: FFI -- creating cross engine rubygems

142 void Init_simple_metrics_ext()143 {144     VALUE cSM_Common;145 146     mSM  = rb_define_module( "SimpleMetrics" );147     mSME = rb_define_module_under( mSM, "Ext" );148 149     /* load the class we inherit from */150     rb_require("simplemetrics/common");151 152     cSM_Common = rb_const_get( mSM, rb_intern( "Common" ) );153 154     cSME_Metric = rb_define_class_under( mSME, "Metric", cSM_Common );155 156     rb_define_alloc_func(cSME_Metric, sm_alloc); 157     rb_define_method( cSME_Metric, "initialize", sm_initialize, 1 );158     rb_define_method( cSME_Metric, "update", sm_update, 1 );159     rb_define_method( cSME_Metric, "count", sm_count, 0 );160     rb_define_method( cSME_Metric, "max", sm_max, 0 );161     rb_define_method( cSME_Metric, "min", sm_min, 0 );162     rb_define_method( cSME_Metric, "mean", sm_mean, 0 );163     rb_define_method( cSME_Metric, "rate", sm_rate, 0 );164     rb_define_method( cSME_Metric, "sum", sm_sum, 0 );165     rb_define_method( cSME_Metric, "stddev", sm_stddev, 0 );166 167 }

17Tuesday, November 24, 2009

Page 26: FFI -- creating cross engine rubygems

142 void Init_simple_metrics_ext()143 {144     VALUE cSM_Common;145 146     mSM  = rb_define_module( "SimpleMetrics" );147     mSME = rb_define_module_under( mSM, "Ext" );148 149     /* load the class we inherit from */150     rb_require("simplemetrics/common");151 152     cSM_Common = rb_const_get( mSM, rb_intern( "Common" ) );153 154     cSME_Metric = rb_define_class_under( mSME, "Metric", cSM_Common );155 156     rb_define_alloc_func(cSME_Metric, sm_alloc); 157     rb_define_method( cSME_Metric, "initialize", sm_initialize, 1 );158     rb_define_method( cSME_Metric, "update", sm_update, 1 );159     rb_define_method( cSME_Metric, "count", sm_count, 0 );160     rb_define_method( cSME_Metric, "max", sm_max, 0 );161     rb_define_method( cSME_Metric, "min", sm_min, 0 );162     rb_define_method( cSME_Metric, "mean", sm_mean, 0 );163     rb_define_method( cSME_Metric, "rate", sm_rate, 0 );164     rb_define_method( cSME_Metric, "sum", sm_sum, 0 );165     rb_define_method( cSME_Metric, "stddev", sm_stddev, 0 );166 167 }

17Tuesday, November 24, 2009

Page 27: FFI -- creating cross engine rubygems

7 module SimpleMetrics 8   module FFI 9 10     class Struct < ::FFI::ManagedStruct11       layout :min, :double, :max, :double, :sum, :double, 12              :sumsq, :double, :count, :long13       def self.release( ptr )14         SimpleMetrics::FFI.simple_metrics_free( ptr )15       end16     end17 18     extend ::FFI::Library19     ffi_lib "libsimple_metrics"20 21     attach_function :simple_metrics_new,   [          ], :pointer22     attach_function :simple_metrics_free,  [ :pointer ], :void23     attach_function :simple_metrics_update,[ :pointer, :double  ], :void24     attach_function :simple_metrics_min,   [ :pointer ], :double25     attach_function :simple_metrics_max,   [ :pointer ], :double26     attach_function :simple_metrics_mean,  [ :pointer ], :double27     attach_function :simple_metrics_sum,   [ :pointer ], :double28     attach_function :simple_metrics_count, [ :pointer ], :long29     attach_function :simple_metrics_stddev,[ :pointer ], :double30     attach_function :simple_metrics_rate,  [ :pointer ], :double31 32   end33 end

18Tuesday, November 24, 2009

Page 28: FFI -- creating cross engine rubygems

7 module SimpleMetrics 8   module FFI 9 10     class Struct < ::FFI::ManagedStruct11       layout :min, :double, :max, :double, :sum, :double, 12              :sumsq, :double, :count, :long13       def self.release( ptr )14         SimpleMetrics::FFI.simple_metrics_free( ptr )15       end16     end17 18     extend ::FFI::Library19     ffi_lib "libsimple_metrics"20 21     attach_function :simple_metrics_new,   [          ], :pointer22     attach_function :simple_metrics_free,  [ :pointer ], :void23     attach_function :simple_metrics_update,[ :pointer, :double  ], :void24     attach_function :simple_metrics_min,   [ :pointer ], :double25     attach_function :simple_metrics_max,   [ :pointer ], :double26     attach_function :simple_metrics_mean,  [ :pointer ], :double27     attach_function :simple_metrics_sum,   [ :pointer ], :double28     attach_function :simple_metrics_count, [ :pointer ], :long29     attach_function :simple_metrics_stddev,[ :pointer ], :double30     attach_function :simple_metrics_rate,  [ :pointer ], :double31 32   end33 end

library function parameters return type

18Tuesday, November 24, 2009

Page 29: FFI -- creating cross engine rubygems

35 module SimpleMetrics36   module FFI37     class Metric < ::SimpleMetrics::Common38       include ::SimpleMetrics::FFI39       def initialize( name )40         super41         @impl = FFI.simple_metrics_new42       end43 44       def update( v )45         simple_metrics_update( @impl, v )46       end47 48       self.keys.each do |f|49         module_eval <<-code50         def #{f}51           simple_metrics_#{f}( @impl )52         end53         code54       end55     end56   end57 end

19Tuesday, November 24, 2009

Page 30: FFI -- creating cross engine rubygems

10 class Struct < ::FFI::ManagedStruct11   layout :min, :double, 12          :max, :double, 13          :sum, :double, 14          :sumsq, :double, 15          :count, :long16   def self.release( ptr )17     SimpleMetrics::FFI.simple_metrics_free( ptr )18   end19 end

 6 typedef struct _simple_metrics{ 7     double min; 8     double max; 9     double sum;10     double sumsq;11     long   count;12 } simple_metrics ;

20Tuesday, November 24, 2009

Page 31: FFI -- creating cross engine rubygems

14 simple_metrics* simple_metrics_new();15 void            simple_metrics_free(   simple_metrics* sm );16 void            simple_metrics_update( simple_metrics* sm, double value );17 double          simple_metrics_mean(   simple_metrics* sm );18 double          simple_metrics_min(    simple_metrics* sm );19 double          simple_metrics_max(    simple_metrics* sm );20 double          simple_metrics_sum(    simple_metrics* sm );21 long            simple_metrics_count(  simple_metrics* sm );22 double          simple_metrics_stddev( simple_metrics* sm );23 double          simple_metrics_rate(   simple_metrics* sm );

21 extend ::FFI::Library22 ffi_lib "libsimple_metrics"23 24 attach_function :simple_metrics_new,   [          ], :pointer25 attach_function :simple_metrics_free,  [ :pointer ], :void26 attach_function :simple_metrics_update,[ :pointer, :double  ], :void27 attach_function :simple_metrics_mean,  [ :pointer ], :double28 attach_function :simple_metrics_min,   [ :pointer ], :double29 attach_function :simple_metrics_max,   [ :pointer ], :double30 attach_function :simple_metrics_sum,   [ :pointer ], :double31 attach_function :simple_metrics_count, [ :pointer ], :long32 attach_function :simple_metrics_stddev,[ :pointer ], :double33 attach_function :simple_metrics_rate,  [ :pointer ], :double

21Tuesday, November 24, 2009

Page 32: FFI -- creating cross engine rubygems

FFI Traditional C Extension

Jeremy’s Time

1 Million update() calls

Will it Blend run on JRuby?

40 minutes 70 minutes

1.16 sec 0.42 sec

Yes No

MEANINGLESS METRICS

22Tuesday, November 24, 2009

Page 33: FFI -- creating cross engine rubygems

DEMO

23Tuesday, November 24, 2009

Page 34: FFI -- creating cross engine rubygems

% ruby example/bench.rb 1000000generating 1000000 random numbers between 0 and 10,000Rehearsal -----------------------------------------------ext 0.500000 0.000000 0.500000 ( 0.504501)ffi 0.960000 0.000000 0.960000 ( 0.956847)-------------------------------------- total: 1.460000sec

user system total realext 0.510000 0.000000 0.510000 ( 0.521388)ffi 0.940000 0.000000 0.940000 ( 0.946538)

% ~/pkgs/jruby-1.4.0/bin/jruby example/bench.rb 1000000generating 1000000 random numbers between 0 and 10,000Rehearsal -----------------------------------------------ffi 0.697000 0.000000 0.697000 ( 0.696000)-------------------------------------- total: 0.697000sec

user system total realffi 0.493000 0.000000 0.493000 ( 0.493000)

24Tuesday, November 24, 2009

Page 35: FFI -- creating cross engine rubygems

FFI BASED PROJECTS

Nokogiri ffi-wiiuse

Gnu Linear Programming Toolkit NiceFFI

Ruby-SDL-FFI

Rubygameffi-inliner

ffi-opengl

ffi-life

ffi-tccJohnson FFI port

rufus-tokyo

ffi-tk

Ruby-GIR-FFI

ffi-opencl

25Tuesday, November 24, 2009

Page 36: FFI -- creating cross engine rubygems

THANKS

•Wayne Meissner - adding libffi to JRuby and providing the ffi gem for MRI.

• Evan Phoenix - using libffi in Rubinius

• Authors of the examples on the ffi project wiki

26Tuesday, November 24, 2009

Page 37: FFI -- creating cross engine rubygems

LINKS

• http://github.com/ffi/ffi

• http://wmeissner.blogspot.com/

• http://groups.google.com/group/ruby-ffi

27Tuesday, November 24, 2009

Page 38: FFI -- creating cross engine rubygems

QUESTIONS?

28Tuesday, November 24, 2009