From: Jamie Herre Date: 2002-07-14T09:39:32+09:00 Subject: speed improvements during ruby_init Hi, Messing around with gprof after being agitated by a recent thread regarding ruby IO, I found that the init phase is taking longer than necessary due to the following: 1) rb_clear_cache_by_id( ) is called many times during ruby init and parsing. each call entails 0x800 comparisons. Proposed solution is to disable the method cache until ruby_run( ). 2) Although it may be necessary to rb_gc( ) after load_file( ), it is not always so for small scripts. Proposed solution is to trigger gc based on malloc_memories. These two changes can make short scripts run noticeably faster: $ time ../ruby172/ruby hello_world.rb > /dev/null real 0m0.028s user 0m0.023s sys 0m0.000s $ time ../ruby_dev/ruby hello_world.rb > /dev/null real 0m0.011s user 0m0.008s sys 0m0.000s $ time ../ruby172/ruby lib/cgi.rb real 0m0.061s user 0m0.055s sys 0m0.000s $ time ../ruby_dev/ruby lib/cgi.rb real 0m0.034s user 0m0.031s sys 0m0.000s -J Would anyone care to check this? Is this adding extraneous overhead to the method cache? Jamie Herre Gettys Group Software, Inc. Index: eval.c =================================================================== RCS file: /src/ruby/eval.c,v retrieving revision 1.303 diff -u -r1.303 eval.c --- eval.c 2002/06/24 07:20:42 1.303 +++ eval.c 2002/07/14 00:10:18 @@ -189,6 +189,8 @@ }; static struct cache_entry cache[CACHE_SIZE]; +static int cache_enabled = 0; +/* must call rb_clear_cache when set cache_enabled = 1 */ void rb_clear_cache() @@ -246,7 +248,7 @@ rb_raise(rb_eSecurityError, "Insecure: can't define method"); } if (OBJ_FROZEN(klass)) rb_error_frozen("class/module"); - rb_clear_cache_by_id(mid); + if (cache_enabled) rb_clear_cache_by_id(mid); body = NEW_METHOD(node, noex); st_insert(RCLASS(klass)->m_tbl, mid, body); } @@ -280,6 +282,7 @@ NODE * volatile body; struct cache_entry *ent; + /* purposely ignoring cache_enabled here for simplicity */ if ((body = search_method(klass, id, &origin)) == 0 || !body->nd_body) { /* store empty info in cache */ ent = cache + EXPR1(klass, id); @@ -333,7 +336,7 @@ rb_name_error(mid, "method `%s' not defined in %s", rb_id2name(mid), rb_class2name(klass)); } - rb_clear_cache_by_id(mid); + if (cache_enabled) rb_clear_cache_by_id(mid); if (FL_TEST(klass, FL_SINGLETON)) { rb_funcall(rb_iv_get(klass, "__attached__"), singleton_removed, 1, ID2SYM(mid)); } @@ -440,7 +443,7 @@ /* is it in the method cache? */ ent = cache + EXPR1(klass, id); - if (ent->mid == id && ent->klass == klass) { + if (ent->mid == id && ent->klass == klass && cache_enabled) { if (ex && (ent->noex & NOEX_PRIVATE)) return Qfalse; if (!ent->method) return Qfalse; @@ -1228,6 +1231,10 @@ if (ruby_nerrs > 0) exit(ruby_nerrs); + /* enable method caching */ + rb_clear_cache(); + cache_enabled = 1; + Init_stack((void*)&tmp); PUSH_TAG(PROT_NONE); PUSH_ITER(ITER_NOT); @@ -1684,7 +1691,7 @@ body = body->nd_head; } - rb_clear_cache_by_id(name); + if (cache_enabled) rb_clear_cache_by_id(name); st_insert(RCLASS(klass)->m_tbl, name, NEW_METHOD(NEW_FBODY(body, def, origin), orig->nd_noex)); if (FL_TEST(klass, FL_SINGLETON)) { @@ -4698,7 +4705,7 @@ } /* is it in the method cache? */ ent = cache + EXPR1(klass, mid); - if (ent->mid == mid && ent->klass == klass) { + if (ent->mid == mid && ent->klass == klass && cache_enabled) { if (!ent->method) return rb_undefined(recv, mid, argc, argv, scope==2?CSTAT_VCALL:0); klass = ent->origin; @@ -5650,7 +5657,7 @@ for (i=0; i (GC_MALLOC_LIMIT/2)) rb_gc(); +} + VALUE rb_gc_start() { Index: ruby.c =================================================================== RCS file: /src/ruby/ruby.c,v retrieving revision 1.62 diff -u -r1.62 ruby.c --- ruby.c 2002/05/29 05:20:33 1.62 +++ ruby.c 2002/07/14 00:10:20 @@ -854,7 +854,7 @@ else if (f != rb_stdin) { rb_io_close(f); } - rb_gc(); + rb_gc_ifneeded(); } void