ruby-changes:40254
From: normal <ko1@a...>
Date: Thu, 29 Oct 2015 10:14:57 +0900 (JST)
Subject: [ruby-changes:40254] normal:r52335 (trunk): variable.c: reduce heap usage for autoload_data_i
normal 2015-10-29 10:14:45 +0900 (Thu, 29 Oct 2015) New Revision: 52335 http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=revision&revision=52335 Log: variable.c: reduce heap usage for autoload_data_i For the loader (first thread which hits autoload, it is wasteful to have extra elements on the stack. For the lifetime of the process, it is wasteful to waste 2 words for every autoload entry. So this makes full use of existing stack overhead while reducing heap overhead for long-lived autoload_data_i structs. * variable.c (struct autoload_state): usable as wait-queue head (struct autoload_data_i): remove 2 words of overhead (autoload_i_mark): remove marking for thread (autoload_reset): adjust for struct changes (rb_autoload): ditto (rb_autoloading_value): ditto (rb_autoload_load): ditto (const_update): ditto Modified files: trunk/ChangeLog trunk/variable.c Index: ChangeLog =================================================================== --- ChangeLog (revision 52334) +++ ChangeLog (revision 52335) @@ -1,3 +1,14 @@ https://github.com/ruby/ruby/blob/trunk/ChangeLog#L1 +Thu Oct 29 10:08:33 2015 Eric Wong <e@8...> + + * variable.c (struct autoload_state): usable as wait-queue head + (struct autoload_data_i): remove 2 words of overhead + (autoload_i_mark): remove marking for thread + (autoload_reset): adjust for struct changes + (rb_autoload): ditto + (rb_autoloading_value): ditto + (rb_autoload_load): ditto + (const_update): ditto + Thu Oct 29 08:48:05 2015 Eric Wong <e@8...> * variable.c (struct autoload_data_i): add waitq_head Index: variable.c =================================================================== --- variable.c (revision 52334) +++ variable.c (revision 52335) @@ -1892,12 +1892,24 @@ autoload_data(VALUE mod, ID id) https://github.com/ruby/ruby/blob/trunk/variable.c#L1892 return (VALUE)val; } +/* always on stack, no need to mark */ +struct autoload_state { + struct autoload_data_i *ele; + VALUE mod; + VALUE result; + ID id; + VALUE thread; + union { + struct list_node node; + struct list_head head; + } waitq; +}; + struct autoload_data_i { VALUE feature; int safe_level; - VALUE thread; VALUE value; - struct list_head waitq_head; /* links autoload_state.waitq_node */ + struct autoload_state *state; /* points to on-stack struct */ }; static void @@ -1905,7 +1917,6 @@ autoload_i_mark(void *ptr) https://github.com/ruby/ruby/blob/trunk/variable.c#L1917 { struct autoload_data_i *p = ptr; rb_gc_mark(p->feature); - rb_gc_mark(p->thread); rb_gc_mark(p->value); } @@ -1965,8 +1976,8 @@ rb_autoload(VALUE mod, ID id, const char https://github.com/ruby/ruby/blob/trunk/variable.c#L1976 ad = TypedData_Make_Struct(0, struct autoload_data_i, &autoload_data_i_type, ele); ele->feature = fn; ele->safe_level = rb_safe_level(); - ele->thread = Qnil; ele->value = Qundef; + ele->state = 0; st_insert(tbl, (st_data_t)id, (st_data_t)ad); } @@ -2039,7 +2050,7 @@ rb_autoloading_value(VALUE mod, ID id, V https://github.com/ruby/ruby/blob/trunk/variable.c#L2050 if (!(load = autoload_data(mod, id)) || !(ele = check_autoload_data(load))) { return 0; } - if (ele->thread == rb_thread_current()) { + if (ele->state && ele->state->thread == rb_thread_current()) { if (ele->value != Qundef) { if (value) { *value = ele->value; @@ -2079,16 +2090,6 @@ autoload_const_set(VALUE arg) https://github.com/ruby/ruby/blob/trunk/variable.c#L2090 return 0; /* ignored */ } -/* this is on the thread stack, not heap */ -struct autoload_state { - struct autoload_data_i *ele; - VALUE mod; - VALUE result; - ID id; - struct list_node waitq_node; /* links autoload_data_i.waitq_head */ - VALUE waiting_th; -}; - static VALUE autoload_require(VALUE arg) { @@ -2107,9 +2108,9 @@ autoload_reset(VALUE arg) https://github.com/ruby/ruby/blob/trunk/variable.c#L2108 struct autoload_state *state = (struct autoload_state *)arg; int need_wakeups = 0; - if (state->ele->thread == rb_thread_current()) { + if (state->ele->state == state) { need_wakeups = 1; - state->ele->thread = Qnil; + state->ele->state = 0; } /* At the last, move a value defined in autoload to constant table */ @@ -2130,11 +2131,11 @@ autoload_reset(VALUE arg) https://github.com/ruby/ruby/blob/trunk/variable.c#L2131 if (need_wakeups) { struct autoload_state *cur, *nxt; - list_for_each_safe(&state->ele->waitq_head, cur, nxt, waitq_node) { - VALUE th = cur->waiting_th; + list_for_each_safe(&state->waitq.head, cur, nxt, waitq.node) { + VALUE th = cur->thread; - cur->waiting_th = Qfalse; - list_del(&cur->waitq_node); + cur->thread = Qfalse; + list_del(&cur->waitq.node); /* * cur is stored on the stack of cur->waiting_th, @@ -2161,7 +2162,7 @@ rb_autoload_load(VALUE mod, ID id) https://github.com/ruby/ruby/blob/trunk/variable.c#L2162 src = rb_sourcefile(); if (src && loading && strcmp(src, loading) == 0) return Qfalse; - /* set ele->thread for a marker of autoloading thread */ + /* set ele->state for a marker of autoloading thread */ if (!(ele = check_autoload_data(load))) { return Qfalse; } @@ -2169,25 +2170,25 @@ rb_autoload_load(VALUE mod, ID id) https://github.com/ruby/ruby/blob/trunk/variable.c#L2170 state.ele = ele; state.mod = mod; state.id = id; - if (ele->thread == Qnil) { - ele->thread = rb_thread_current(); + state.thread = rb_thread_current(); + if (!ele->state) { + ele->state = &state; /* * autoload_reset will wake up any threads added to this * iff the GVL is released during autoload_require */ - list_head_init(&ele->waitq_head); + list_head_init(&state.waitq.head); } else { - state.waiting_th = rb_thread_current(); - list_add_tail(&ele->waitq_head, &state.waitq_node); + list_add_tail(&ele->state->waitq.head, &state.waitq.node); /* * autoload_reset in other thread will resume us and remove us * from the waitq list */ do { rb_thread_sleep_deadly(); - } while (state.waiting_th != Qfalse); + } while (state.thread != Qfalse); } /* autoload_data_i can be deleted by another thread while require */ @@ -2594,8 +2595,8 @@ const_update(st_data_t *key, st_data_t * https://github.com/ruby/ruby/blob/trunk/variable.c#L2595 load = autoload_data(klass, id); /* for autoloading thread, keep the defined value to autoloading storage */ - if (load && (ele = check_autoload_data(load)) && - (ele->thread == rb_thread_current())) { + if (load && (ele = check_autoload_data(load)) && ele->state && + (ele->state->thread == rb_thread_current())) { rb_clear_constant_cache(); ele->value = val; /* autoload_i is non-WB-protected */ -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/