mrubyのattr系メソッドの速度

mrubyのbenchmarkディレクトリにao-render.rb(AOベンチ)があるわけだが、このコードを見ると例えばVevtorクラスのgetter/setterは自前で定義されている。作った人(@miura1729さん)によると元はytl用のものでattr系メソッドが無かったからということなのだが、これをattr系で定義したら速くなるのだろうか。
Ruby1.9.3では速くなる。

require 'benchmark'

class Foo
  def hoge
    @hoge
  end
  def hoge=(v)
    @hoge = v
  end
end

class Bar
  attr_accessor :fuga
end

Benchmark.bm do |bm|
  bm.report("Foo#hoge") do
    a = Foo.new
    1000000.times do
      a.hoge = a.hoge
    end
  end
  bm.report("Bar#fuga") do
    b = Bar.new
    1000000.times do
      b.fuga = b.fuga
    end
  end
end
#=>              user     system      total        real
#  Foo#hoge  0.235000   0.000000   0.235000 (  0.234375)
#  Bar#fuga  0.187000   0.000000   0.187000 (  0.187500)

が、mrubyではとても遅くなる。

class Foo
  def hoge
    @hoge
  end
  def hoge=(v)
    @hoge = v
  end
end

class Bar
  attr_accessor :fuga
end

from = Time.new
a = Foo.new
1000000.times do
  a.hoge = a.hoge
end
p Time.new - from
from = Time.new
b = Bar.new
1000000.times do
  b.fuga = b.fuga
end
p Time.new - from

#=>0.59375
#  2.453125

なぜかと言うと、getter/setterがself.instance_variable_get/setを呼ぶRubyコードのメソッドとして定義されるからで、mrblibの中に含まれているからである。単純にインスタンス変数を参照する固定コードと比べると当然遅い。evalがあればそれで定義して同等の速度にまで持っていけるのだろうが、それでも同等にしかならない。
これをCで書いたら速くなるだろうか。試してみた。とりあえずreaderだけ。

static mrb_value
mrb_attr_reader_func(mrb_state *mrb, mrb_value self)
{
  mrb_sym sym = mrb->ci->mid;
  int len;
  const char *name;
  name = mrb_sym2name_len(mrb, sym, &len);

  {
    char buf[len + 2];
    buf[0] = '@';
    memcpy(buf + 1, name, len + 1);
    return mrb_iv_get(mrb, self, mrb_intern2(mrb, buf, len + 1));
  }
  return mrb_nil_value();
}

static mrb_value
mrb_mod_attr_reader(mrb_state *mrb, mrb_value mod)
{
  mrb_value obj;
  const char *name;
  int len;

  mrb_get_args(mrb, "o", &obj);

  if (mrb_type(obj) != MRB_TT_SYMBOL) {
    mrb_value str = mrb_funcall(mrb, obj, "inspect", 0);
    mrb_raise(mrb, E_TYPE_ERROR, "%s is not a symbol", mrb_string_value_ptr(mrb, str));
  }

  if (!(name = mrb_sym2name_len(mrb, mrb_symbol(obj), &len))) {
    return mrb_nil_value();
  }

  mrb_define_method(mrb, mrb_class_ptr(mod), name, mrb_attr_reader_func, ARGS_NONE());

  return mrb_nil_value();
}
0.59375
2.9375

前より遅くなった。
mrubyではCで定義されたメソッドの呼び出しは非常に速い。Procオブジェクトとして存在して、シンプルに関節分岐でVMから直接呼び出す仕掛けになっている。OP_ENTERもOP_RETURNもいらない。引数が無ければスタックを参照することも無い。
それがなぜ遅いのかというと、恐らくメソッド名のシンボルから文字列を取り出し、@をつけてシンボル化→インスタンス変数アクセスというハッシュ計算しまくりな構造になってしまうからではないかと思う。これをうまいことカットできれば速くのなるのかもしれないが、とりあえずいい手は思い浮かばないので諦め。