Mae向きなブログ

Mae向きな情報発信を続けていきたいと思います。

拡張ライブラリ作成練習

せっかく、PerlからC言語の関数を呼び出す方法が分かったので、同じようなことをRubyでもやってみました。参考にしたのは、以下のサイトです。
http://ruby.gfd-dennou.org/tutorial/ruby-ext/

  • prime.cを作成
long isprime(long lObj)
{
  long j;
  for (j = 2; j*j <= lObj; ++j) {
    if (lObj % j == 0)
      return 0;
  }
  return 1;
}

long prime(long lLmtCnt)
{
  long lCnt = 0, lObj;
  for (lObj = 2; lCnt < lLmtCnt; ++lObj) {
    lCnt += isprime(lObj);
  }
  return lObj - 1;
}
  • Swigに対する入力ファイルとしてprime.iを作成
%module Prime
%{
%}

extern int prime(long lLmtCnt);
  • Swigで処理してラッパー関数を作成
$ swig -ruby prime.i
  • extconf.rbの作成
require 'mkmf'
create_makefile('Prime')
mmasa@debian:~/work/ruby/ext/Prime/swig$ ruby extconf.rb
creating Makefile
mmasa@debian:~/work/ruby/ext/Prime/swig$ make
  • 動作テスト(Prime_ext.rb)
#!/usr/bin/ruby
require 'Prime'

print Prime.prime(ARGV[0].to_i), "\n"
mmasa@debian:~/work/ruby/ext/Prime/swig$ ruby Prime_ext.rb 100
541

Prime.rbとPrime_ext.rbの比較

Prime.rbは以下です。

#!/usr/bin/ruby

def isprime(lObj)
  j = 2
  while j*j <= lObj
    return 0 if (lObj % j) == 0
    j+=1
  end 
  return 1
end

def prime(lLmtCnt)
  lCnt = 0
  lObj = 2
  while lCnt < lLmtCnt
    lCnt += isprime(lObj)
    lObj += 1
  end
  return lObj - 1
end

print prime(ARGV[0].to_i), "\n"

Bench.plは以下です。

#!/usr/bin/perl -w
use strict;
use Benchmark;
timethese(200, {
    'Ruby    :' => "system('ruby Prime.rb 2000 > /dev/null')",
    'Ruby-ext:' => "system('(cd swig;ruby Prime_ext.rb 2000 > /dev/null)')",
});

実行結果は以下のようになりました。Prime.rbの書き方がまずいということもあると思いますが、両者には相当な性能差があることが分かりました。

mmasa@debian:~/work/ruby/ext/Prime$ perl Bench.pl
Benchmark: timing 200 iterations of Ruby    :, Ruby-ext:...
 Ruby    :: 59 wallclock secs ( 0.00 usr  0.05 sys + 49.19 cusr  6.62 csys = 55.86 CPU) @ 4000.00/s (n=200)
 Ruby-ext::  2 wallclock secs ( 0.00 usr  0.05 sys +  0.18 cusr  2.29 csys =  2.52 CPU) @ 4000.00/s (n=200)