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 git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@52335 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
parent
d90f9b1481
commit
fc87b3b7e0
11
ChangeLog
11
ChangeLog
@ -1,3 +1,14 @@
|
|||||||
|
Thu Oct 29 10:08:33 2015 Eric Wong <e@80x24.org>
|
||||||
|
|
||||||
|
* 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@80x24.org>
|
Thu Oct 29 08:48:05 2015 Eric Wong <e@80x24.org>
|
||||||
|
|
||||||
* variable.c (struct autoload_data_i): add waitq_head
|
* variable.c (struct autoload_data_i): add waitq_head
|
||||||
|
61
variable.c
61
variable.c
@ -1892,12 +1892,24 @@ autoload_data(VALUE mod, ID id)
|
|||||||
return (VALUE)val;
|
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 {
|
struct autoload_data_i {
|
||||||
VALUE feature;
|
VALUE feature;
|
||||||
int safe_level;
|
int safe_level;
|
||||||
VALUE thread;
|
|
||||||
VALUE value;
|
VALUE value;
|
||||||
struct list_head waitq_head; /* links autoload_state.waitq_node */
|
struct autoload_state *state; /* points to on-stack struct */
|
||||||
};
|
};
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -1905,7 +1917,6 @@ autoload_i_mark(void *ptr)
|
|||||||
{
|
{
|
||||||
struct autoload_data_i *p = ptr;
|
struct autoload_data_i *p = ptr;
|
||||||
rb_gc_mark(p->feature);
|
rb_gc_mark(p->feature);
|
||||||
rb_gc_mark(p->thread);
|
|
||||||
rb_gc_mark(p->value);
|
rb_gc_mark(p->value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1965,8 +1976,8 @@ rb_autoload(VALUE mod, ID id, const char *file)
|
|||||||
ad = TypedData_Make_Struct(0, struct autoload_data_i, &autoload_data_i_type, ele);
|
ad = TypedData_Make_Struct(0, struct autoload_data_i, &autoload_data_i_type, ele);
|
||||||
ele->feature = fn;
|
ele->feature = fn;
|
||||||
ele->safe_level = rb_safe_level();
|
ele->safe_level = rb_safe_level();
|
||||||
ele->thread = Qnil;
|
|
||||||
ele->value = Qundef;
|
ele->value = Qundef;
|
||||||
|
ele->state = 0;
|
||||||
st_insert(tbl, (st_data_t)id, (st_data_t)ad);
|
st_insert(tbl, (st_data_t)id, (st_data_t)ad);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2039,7 +2050,7 @@ rb_autoloading_value(VALUE mod, ID id, VALUE* value)
|
|||||||
if (!(load = autoload_data(mod, id)) || !(ele = check_autoload_data(load))) {
|
if (!(load = autoload_data(mod, id)) || !(ele = check_autoload_data(load))) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (ele->thread == rb_thread_current()) {
|
if (ele->state && ele->state->thread == rb_thread_current()) {
|
||||||
if (ele->value != Qundef) {
|
if (ele->value != Qundef) {
|
||||||
if (value) {
|
if (value) {
|
||||||
*value = ele->value;
|
*value = ele->value;
|
||||||
@ -2079,16 +2090,6 @@ autoload_const_set(VALUE arg)
|
|||||||
return 0; /* ignored */
|
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
|
static VALUE
|
||||||
autoload_require(VALUE arg)
|
autoload_require(VALUE arg)
|
||||||
{
|
{
|
||||||
@ -2107,9 +2108,9 @@ autoload_reset(VALUE arg)
|
|||||||
struct autoload_state *state = (struct autoload_state *)arg;
|
struct autoload_state *state = (struct autoload_state *)arg;
|
||||||
int need_wakeups = 0;
|
int need_wakeups = 0;
|
||||||
|
|
||||||
if (state->ele->thread == rb_thread_current()) {
|
if (state->ele->state == state) {
|
||||||
need_wakeups = 1;
|
need_wakeups = 1;
|
||||||
state->ele->thread = Qnil;
|
state->ele->state = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* At the last, move a value defined in autoload to constant table */
|
/* At the last, move a value defined in autoload to constant table */
|
||||||
@ -2130,11 +2131,11 @@ autoload_reset(VALUE arg)
|
|||||||
if (need_wakeups) {
|
if (need_wakeups) {
|
||||||
struct autoload_state *cur, *nxt;
|
struct autoload_state *cur, *nxt;
|
||||||
|
|
||||||
list_for_each_safe(&state->ele->waitq_head, cur, nxt, waitq_node) {
|
list_for_each_safe(&state->waitq.head, cur, nxt, waitq.node) {
|
||||||
VALUE th = cur->waiting_th;
|
VALUE th = cur->thread;
|
||||||
|
|
||||||
cur->waiting_th = Qfalse;
|
cur->thread = Qfalse;
|
||||||
list_del(&cur->waitq_node);
|
list_del(&cur->waitq.node);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* cur is stored on the stack of cur->waiting_th,
|
* cur is stored on the stack of cur->waiting_th,
|
||||||
@ -2161,7 +2162,7 @@ rb_autoload_load(VALUE mod, ID id)
|
|||||||
src = rb_sourcefile();
|
src = rb_sourcefile();
|
||||||
if (src && loading && strcmp(src, loading) == 0) return Qfalse;
|
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))) {
|
if (!(ele = check_autoload_data(load))) {
|
||||||
return Qfalse;
|
return Qfalse;
|
||||||
}
|
}
|
||||||
@ -2169,25 +2170,25 @@ rb_autoload_load(VALUE mod, ID id)
|
|||||||
state.ele = ele;
|
state.ele = ele;
|
||||||
state.mod = mod;
|
state.mod = mod;
|
||||||
state.id = id;
|
state.id = id;
|
||||||
if (ele->thread == Qnil) {
|
state.thread = rb_thread_current();
|
||||||
ele->thread = rb_thread_current();
|
if (!ele->state) {
|
||||||
|
ele->state = &state;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* autoload_reset will wake up any threads added to this
|
* autoload_reset will wake up any threads added to this
|
||||||
* iff the GVL is released during autoload_require
|
* iff the GVL is released during autoload_require
|
||||||
*/
|
*/
|
||||||
list_head_init(&ele->waitq_head);
|
list_head_init(&state.waitq.head);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
state.waiting_th = rb_thread_current();
|
list_add_tail(&ele->state->waitq.head, &state.waitq.node);
|
||||||
list_add_tail(&ele->waitq_head, &state.waitq_node);
|
|
||||||
/*
|
/*
|
||||||
* autoload_reset in other thread will resume us and remove us
|
* autoload_reset in other thread will resume us and remove us
|
||||||
* from the waitq list
|
* from the waitq list
|
||||||
*/
|
*/
|
||||||
do {
|
do {
|
||||||
rb_thread_sleep_deadly();
|
rb_thread_sleep_deadly();
|
||||||
} while (state.waiting_th != Qfalse);
|
} while (state.thread != Qfalse);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* autoload_data_i can be deleted by another thread while require */
|
/* autoload_data_i can be deleted by another thread while require */
|
||||||
@ -2594,8 +2595,8 @@ const_update(st_data_t *key, st_data_t *value, st_data_t arg, int existing)
|
|||||||
|
|
||||||
load = autoload_data(klass, id);
|
load = autoload_data(klass, id);
|
||||||
/* for autoloading thread, keep the defined value to autoloading storage */
|
/* for autoloading thread, keep the defined value to autoloading storage */
|
||||||
if (load && (ele = check_autoload_data(load)) &&
|
if (load && (ele = check_autoload_data(load)) && ele->state &&
|
||||||
(ele->thread == rb_thread_current())) {
|
(ele->state->thread == rb_thread_current())) {
|
||||||
rb_clear_constant_cache();
|
rb_clear_constant_cache();
|
||||||
|
|
||||||
ele->value = val; /* autoload_i is non-WB-protected */
|
ele->value = val; /* autoload_i is non-WB-protected */
|
||||||
|
Loading…
x
Reference in New Issue
Block a user