From: Shumpei Akai Date: 2008-12-03T20:38:31+09:00 Subject: [ruby-dev:37265] [Feature #819] Caching Symbol#to_proc Feature #819: Caching Symbol#to_proc http://redmine.ruby-lang.org/issues/show/819 起票者: Shumpei Akai ステータス: Open, 優先度: Normal Symbol#to_proc で毎回オブジェクトを生成するのは無駄な気がするので, いくつかキャッシュしてはどうでしょうか. require 'benchmark' N=10000 syms_miss=(1..500).map{|i|"a#{i}".to_sym}syms_hit=[:a]*500 Benchmark.bm do|r| r.report("miss"){N.times{syms_miss.each{|x|x.to_proc}}} r.report(" hit"){N.times{syms_hit.each{|x|x.to_proc}}} end 上のようなベンチマークを取ると,次のようになります. % ruby_trunk -v ruby 1.9.1 (2008-12-03 patchlevel 5000 revision 20460) [x86_64-linux] キャッシュなし user system total real miss 9.060000 0.040000 9.100000 ( 9.092981) hit 9.070000 0.050000 9.120000 ( 9.114511) キャッシュあり user system total real miss 9.260000 0.020000 9.280000 ( 9.282089) hit 0.750000 0.000000 0.750000 ( 0.757002) 以下パッチです Index: string.c =================================================================== --- string.c (リビジョン 20465) +++ string.c (作業コピー) @@ -6912,13 +6912,37 @@ * (1..3).collect(&:to_s) #=> ["1", "2", "3"] */ +static VALUE sym_proc_cache=Qfalse; +#define SYM_PROC_CACHE_SIZE 64 static VALUE sym_to_proc(VALUE sym) { - return rb_proc_new(sym_call, (VALUE)SYM2ID(sym)); -} + VALUE proc; + ID id; + long sym_index,proc_index; + VALUE *aryp; + if(!sym_proc_cache){ + rb_global_variable(&sym_proc_cache); + sym_proc_cache = rb_ary_new2(SYM_PROC_CACHE_SIZE * 2); + } + + id=SYM2ID(sym); + sym_index= (id % SYM_PROC_CACHE_SIZE) << 1; + proc_index = sym_index | 1; + + aryp=RARRAY_PTR(sym_proc_cache); + if(aryp[sym_index]==sym){ + return aryp[proc_index]; + }else{ + proc = rb_proc_new(sym_call, (VALUE)id); + aryp[sym_index]=sym; + aryp[proc_index]=proc; + return proc; + } +} + static VALUE sym_succ(VALUE sym) ---------------------------------------- http://redmine.ruby-lang.org