Ruby1.9.1でRubyInline動かした
表題通り。rubyでなるべく高速にプログラムを動かしたかったので、とりあえずRubyInlineをインストールしてサンプルコードを実行してみた。
インストール
- wget http://rubyforge.org/frs/download.php/45683/RubyInline-3.8.1.gem
- sudo gem install --local RubyInline-3.8.1.gem #=>「ZenTestがない」と言われる。
- sudo gem install ZenTest
- sudo gem install --local RubyInline-3.8.1.gem
- 完了
サンプル実行
- 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);
Ruby の拡張ライブラリ開発では RSTRING_PTR, RSTRING_LEN が 1.8.6 から使える
long len = RSTRING_LEN(s);
よって、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 |