Feature #819
closedCaching Symbol#to_proc
Description
=begin
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)
=end
Updated by nobu (Nobuyoshi Nakada) over 16 years ago
=begin
なかだです。
At Thu, 4 Dec 2008 09:48:10 +0900,
Hajime Hoshi wrote in [ruby-dev:37267]:
動作確認はしていないのですが、
sym_proc_cache の生成直後は長さ (len) 0 の配列なので
sym_proc_cache の要素の proc オブジェクトは
GC に回収されてしまう恐れがあるのではないでしょうか。
それと、IDは下位ビットで種類を表しているので値に偏りがあり、キャッ
シュサイズが2の冪乗だと衝突する可能性が高くなります。
--
--- 僕の前にBugはない。
--- 僕の後ろにBugはできる。
中田 伸悦
=end
Updated by akai (Shumpei Akai) over 16 years ago
=begin
赤井です.
Hajime Hoshi wrote, (2008/12/04 9:48):
動作確認はしていないのですが、
sym_proc_cache の生成直後は長さ (len) 0 の配列なので
sym_proc_cache の要素の proc オブジェクトは
GC に回収されてしまう恐れがあるのではないでしょうか。
Nobuyoshi Nakada wrote, (2008/12/04 12:12):
それと、IDは下位ビットで種類を表しているので値に偏りがあり、キャッ
シュサイズが2の冪乗だと衝突する可能性が高くなります。
直すならこのような感じでしょうか.
Index: string.c¶
--- string.c (リビジョン 20513)
+++ string.c (作業コピー)
@@ -6912,13 +6912,38 @@
- (1..3).collect(&:to_s) #=> ["1", "2", "3"]
*/
+static VALUE sym_proc_cache=Qfalse;
+#define SYM_PROC_CACHE_SIZE 67
static VALUE
sym_to_proc(VALUE sym)
{
- return rb_proc_new(sym_call, (VALUE)SYM2ID(sym));
-}
-
VALUE proc;
-
long 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);
-
rb_ary_store(sym_proc_cache, SYM_PROC_CACHE_SIZE*2 - 1, Qnil);
-
}
-
id=SYM2ID(sym)>>3;
-
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)
{
--
赤井駿平
=end
Updated by nobu (Nobuyoshi Nakada) over 16 years ago
- Status changed from Open to Closed
- % Done changed from 0 to 100
=begin
Applied in changeset r20521.
=end
Updated by nobu (Nobuyoshi Nakada) over 16 years ago
=begin
なかだです。
At Thu, 4 Dec 2008 21:19:24 +0900,
Shumpei Akai wrote in [ruby-dev:37274]:
直すならこのような感じでしょうか.
以下の点を変更してコミットしました。
- RDocが混乱するのを防ぐためにsym_proc_cacheを関数内に移した
- rb_gc_register_mark_object()を使う
- idを3bitシフトすると正しくないProcを作ってしまう
--
--- 僕の前にBugはない。
--- 僕の後ろにBugはできる。
中田 伸悦
=end