Ruby1.9.1でRubyInline動かした

表題通り。rubyでなるべく高速にプログラムを動かしたかったので、とりあえずRubyInlineをインストールしてサンプルコードを実行してみた。

環境
Ubuntu8.0.4日本語版 VMware仮想マシン on WindowsXP SP3

インストール

サンプル実行

  • netswitch! | RubyInlineがすごいにあるサンプルコード(長さ10000の文字列同士のxorを1000回取るプログラムが、Rubyだけで書いた場合に対してrubyinlineでどれだけ速くなるかを出力する)を実行。エラー発生。
  • いろいろ調べたところ、MRIのStringクラスを表すstruct RStringの構造が変更されたためのようだ。

Ruby 1.9 からは Array や String の短いものが struct RObject の構造体に埋め込まれるようになった。そのため、struct RString が変更されており、struct RString の C 文字列や文字(バイト)数にアクセスする従来の直接的な方法、

char *p = RSTRING(s)->ptr;
long len = RSTRING(s)->len;

は 1.9 の環境ではコンパイルできなくなっている。

では、どうするか、というと、1.9 からは RSTRING_PTR, RSTRING_LEN というマクロをかましてアクセスする。

char *p = RSTRING_PTR(s);
long len = RSTRING_LEN(s);

Ruby の拡張ライブラリ開発では RSTRING_PTR, RSTRING_LEN が 1.8.6 から使える


よって、netswitch! | RubyInlineがすごいのサンプルコードの当該部分を変更したのが次のコード。

def benchmark
  s = "a" * 10000

  test = Test.new
  t = Time.now
  1000.times{test.string_xor(s, s)}
  Time.now - t
end

class Test
  def string_xor(str1, str2)
    result = str1.clone
    str1.length.times do |i|
      result[i] = str2[i]
    end
    result
  end
end

b1 = benchmark

begin
  require 'inline'
  class Test
    inline do |builder|
      #RSTRING(str1) -> ptr を RSTRING_PTR(str1) に変更。 len も。以下同様。
      builder.c <<-EOF
        VALUE
        string_xor(VALUE str1, VALUE str2)
        {
          VALUE result = rb_str_new(RSTRING_PTR(str1), RSTRING_LEN(str1)); 
          int i;
          for (i = 0; i < RSTRING_LEN(str1); i++) {
            RSTRING_PTR(result)[i]  ^= RSTRING_PTR(str2)[i];
          }
          return result;
        }
      EOF
    end
  end
rescue LoadError
end

b2 = benchmark

p b1/b2

実行結果比較

1.8系サンプルコード 1.9系サンプルコード
実行結果(5回の平均) 240 115
実行時間(5回の平均, 単位s) 9.0 4.3