Skip to content
This repository has been archived by the owner on Jun 21, 2024. It is now read-only.

Commit

Permalink
Make ephemeron marking incremental.
Browse files Browse the repository at this point in the history
Without incremental marking of ephemerons, with sufficiently large
ephemeron list, the GC would fail to make progress as it would start
scanning the ephemeron list from the beginning every time.
  • Loading branch information
kayceesrk committed Sep 10, 2018
1 parent 0c5690c commit df76040
Show file tree
Hide file tree
Showing 5 changed files with 78 additions and 42 deletions.
4 changes: 1 addition & 3 deletions byterun/caml/domain_state.tbl
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,7 @@ DOMAIN_STATE(uintnat, stealing)
DOMAIN_STATE(uintnat, allocated_words)
DOMAIN_STATE(uintnat, swept_words)
DOMAIN_STATE(struct caml__roots_block*, local_roots)
DOMAIN_STATE(value, ephe_list_todo)
DOMAIN_STATE(value, ephe_list_live)
DOMAIN_STATE(uintnat, ephe_cycle)
DOMAIN_STATE(struct caml_ephe_info*, ephe_info)
DOMAIN_STATE(struct caml_final_info*, final_info)
DOMAIN_STATE(intnat, backtrace_pos)
DOMAIN_STATE(intnat, backtrace_active)
Expand Down
11 changes: 11 additions & 0 deletions byterun/caml/weak.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,16 @@

extern value caml_ephe_none;

struct caml_ephe_info {
value todo;
value live;
uintnat cycle;
struct {
value* todop;
uintnat cycle;
} cursor;
};

#define CAML_EPHE_LINK_OFFSET 0
#define CAML_EPHE_DOMAIN_OFFSET 1
#define CAML_EPHE_DATA_OFFSET 2
Expand All @@ -40,6 +50,7 @@ extern value caml_ephe_none;
#define Ephe_domain(e) (*(struct domain**)(Op_val(e) + CAML_EPHE_DOMAIN_OFFSET))
#define Ephe_data(e) (*(Op_val(e) + CAML_EPHE_DATA_OFFSET))

struct caml_ephe_info* caml_alloc_ephe_info (void);
void caml_ephe_clean(struct domain* d, value e);
value caml_bias_ephe_list(value, struct domain*);

Expand Down
18 changes: 9 additions & 9 deletions byterun/domain.c
Original file line number Diff line number Diff line change
Expand Up @@ -791,19 +791,19 @@ static void handover_ephemerons(caml_domain_state* domain_state)
value todo_tail = 0;
value live_tail = 0;

if (domain_state->ephe_list_todo == 0 &&
domain_state->ephe_list_live == 0)
if (domain_state->ephe_info->todo == 0 &&
domain_state->ephe_info->live == 0)
return;

todo_tail = caml_bias_ephe_list(domain_state->ephe_list_todo, (struct domain*)NULL);
live_tail = caml_bias_ephe_list(domain_state->ephe_list_live, (struct domain*)NULL);
caml_add_orphaned_ephe(domain_state->ephe_list_todo, todo_tail,
domain_state->ephe_list_live, live_tail);
if (domain_state->ephe_list_todo != 0) {
todo_tail = caml_bias_ephe_list(domain_state->ephe_info->todo, (struct domain*)NULL);
live_tail = caml_bias_ephe_list(domain_state->ephe_info->live, (struct domain*)NULL);
caml_add_orphaned_ephe(domain_state->ephe_info->todo, todo_tail,
domain_state->ephe_info->live, live_tail);
if (domain_state->ephe_info->todo != 0) {
caml_ephe_todo_list_emptied();
}
domain_state->ephe_list_live = 0;
domain_state->ephe_list_todo = 0;
domain_state->ephe_info->live = 0;
domain_state->ephe_info->todo = 0;
}

static void handover_finalisers(caml_domain_state* domain_state)
Expand Down
75 changes: 47 additions & 28 deletions byterun/major_gc.c
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ extern value caml_ephe_none; /* See weak.c */
struct ephe_cycle_info_t {
atomic_uintnat num_domains_todo;
/* Number of domains that need to scan their ephemerons in the current major
* GC cycle. This field is decremented when ephe_list_todo at a domain
* GC cycle. This field is decremented when ephe_info->todo list at a domain
* becomes empty. */
atomic_uintnat ephe_cycle;
/* Ephemeron cycle count */
Expand Down Expand Up @@ -127,7 +127,7 @@ static void record_ephe_marking_done (uintnat ephe_cycle)

caml_plat_lock(&ephe_lock);
if (ephe_cycle == ephe_cycle_info.ephe_cycle.val) {
Caml_state->ephe_cycle = ephe_cycle;
Caml_state->ephe_info->cycle = ephe_cycle;
ephe_cycle_info.num_domains_done.val++;
}
caml_plat_unlock(&ephe_lock);
Expand Down Expand Up @@ -208,18 +208,18 @@ void caml_adopt_orphaned_work ()

if (orph_structs.ephe_list_live) {
last = caml_bias_ephe_list(orph_structs.ephe_list_live, d);
Ephe_link(last) = domain_state->ephe_list_live;
domain_state->ephe_list_live = orph_structs.ephe_list_live;
Ephe_link(last) = domain_state->ephe_info->live;
domain_state->ephe_info->live = orph_structs.ephe_list_live;
orph_structs.ephe_list_live = 0;
}

if (orph_structs.ephe_list_todo) {
if (domain_state->ephe_list_todo == 0) {
if (domain_state->ephe_info->todo == 0) {
caml_ephe_todo_list_stolen();
}
last = caml_bias_ephe_list(orph_structs.ephe_list_todo, d);
Ephe_link(last) = domain_state->ephe_list_todo;
domain_state->ephe_list_todo = orph_structs.ephe_list_todo;
Ephe_link(last) = domain_state->ephe_info->todo;
domain_state->ephe_info->todo = orph_structs.ephe_list_todo;
orph_structs.ephe_list_todo = 0;
}

Expand Down Expand Up @@ -610,17 +610,23 @@ void caml_darken(void* state, value v, value* ignored) {
}
}

intnat ephe_mark (intnat budget)
intnat ephe_mark (intnat budget, uintnat for_cycle)
{
value v, data, key, f, todo;
value* prev_linkp;
header_t hd;
mlsize_t size, i;
caml_domain_state* domain_state = Caml_state;
int alive_data;
intnat marked = 0, made_live = 0;

todo = domain_state->ephe_list_todo;
prev_linkp = &domain_state->ephe_list_todo;
if (domain_state->ephe_info->cursor.cycle == for_cycle) {
prev_linkp = domain_state->ephe_info->cursor.todop;
todo = *prev_linkp;
} else {
todo = domain_state->ephe_info->todo;
prev_linkp = &domain_state->ephe_info->todo;
}
while (todo != 0 && budget > 0) {
v = todo;
todo = Ephe_link(v);
Expand Down Expand Up @@ -659,15 +665,25 @@ intnat ephe_mark (intnat budget)
if (data != caml_ephe_none && Is_block(data) && is_unmarked(data)) {
caml_darken (0, data, 0);
}
Ephe_link(v) = domain_state->ephe_list_live;
domain_state->ephe_list_live = v;
Ephe_link(v) = domain_state->ephe_info->live;
domain_state->ephe_info->live = v;
*prev_linkp = todo;
made_live++;
} else {
/* Leave this ephemeron on the todo list */
prev_linkp = &Ephe_link(v);
}
marked++;
}

caml_gc_log ("Mark Ephemeron: %s. for ephemeron cycle=%"ARCH_INTNAT_PRINTF_FORMAT"d "
"marked=%"ARCH_INTNAT_PRINTF_FORMAT"d made_live=%"ARCH_INTNAT_PRINTF_FORMAT"d",
domain_state->ephe_info->cursor.cycle == for_cycle ? "continued from cursor" : "discarded cursor",
for_cycle, marked, made_live);

domain_state->ephe_info->cursor.cycle = for_cycle;
domain_state->ephe_info->cursor.todop = prev_linkp;

return budget;
}

Expand All @@ -677,18 +693,18 @@ intnat ephe_sweep (struct domain* d, intnat budget)
value v;
caml_domain_state* domain_state = d->state;

while (domain_state->ephe_list_todo != 0 && budget > 0) {
v = domain_state->ephe_list_todo;
domain_state->ephe_list_todo = Ephe_link(v);
while (domain_state->ephe_info->todo != 0 && budget > 0) {
v = domain_state->ephe_info->todo;
domain_state->ephe_info->todo = Ephe_link(v);
CAMLassert (Tag_val(v) == Abstract_tag);

if (is_unmarked(v)) {
/* The whole array is dead, drop this ephemeron */
budget -= 1;
} else {
caml_ephe_clean(d, v);
Ephe_link(v) = domain_state->ephe_list_live;
domain_state->ephe_list_live = v;
Ephe_link(v) = domain_state->ephe_info->live;
domain_state->ephe_info->live = v;
budget -= Whsize_val(v);
}
}
Expand Down Expand Up @@ -879,11 +895,13 @@ static void cycle_all_domains_callback(struct domain* domain, void* unused)
}

/* Ephemerons */
CAMLassert(domain->state->ephe_list_todo == (value) NULL);
domain->state->ephe_list_todo = domain->state->ephe_list_live;
domain->state->ephe_list_live = (value) NULL;
domain->state->ephe_cycle = 0;
if (domain->state->ephe_list_todo == (value) NULL)
CAMLassert(domain->state->ephe_info->todo == (value) NULL);
domain->state->ephe_info->todo = domain->state->ephe_info->live;
domain->state->ephe_info->live = (value) NULL;
domain->state->ephe_info->cycle = 0;
domain->state->ephe_info->cursor.todop = NULL;
domain->state->ephe_info->cursor.cycle = 0;
if (domain->state->ephe_info->todo == (value) NULL)
caml_ephe_todo_list_emptied();

/* Finalisers */
Expand Down Expand Up @@ -1055,24 +1073,24 @@ static intnat major_collection_slice(intnat howmuch,

/* Ephemerons */
saved_ephe_cycle = atomic_load_acq(&ephe_cycle_info.ephe_cycle);
if (domain_state->ephe_list_todo != (value) NULL &&
saved_ephe_cycle > domain_state->ephe_cycle) {
if (domain_state->ephe_info->todo != (value) NULL &&
saved_ephe_cycle > domain_state->ephe_info->cycle) {
caml_ev_begin("major_gc/ephe_mark");
budget = ephe_mark(budget);
budget = ephe_mark(budget, saved_ephe_cycle);
caml_ev_end("major_gc/ephe_mark");
if (domain_state->ephe_list_todo == (value) NULL)
if (domain_state->ephe_info->todo == (value) NULL)
caml_ephe_todo_list_emptied ();
else if (budget > 0 && domain_state->marking_done)
record_ephe_marking_done(saved_ephe_cycle);
else if (budget > 0) goto mark_again;
}

if (caml_gc_phase == Phase_sweep_ephe &&
domain_state->ephe_list_todo != 0) {
domain_state->ephe_info->todo != 0) {
caml_ev_begin("major_gc/ephe_sweep");
budget = ephe_sweep (d, budget);
caml_ev_end("major_gc/ephe_sweep");
if (domain_state->ephe_list_todo == 0) {
if (domain_state->ephe_info->todo == 0) {
atomic_fetch_add_verify_ge0(&num_domains_to_ephe_sweep, -1);
}
}
Expand Down Expand Up @@ -1347,6 +1365,7 @@ void caml_init_major_gc(caml_domain_state* d) {
d->stealing = 0;
/* Finalisers. Fresh domains participate in updating finalisers. */
d->final_info = caml_alloc_final_info ();
d->ephe_info = caml_alloc_ephe_info();
atomic_fetch_add(&num_domains_to_final_update_first, 1);
atomic_fetch_add(&num_domains_to_final_update_last, 1);
}
Expand Down
12 changes: 10 additions & 2 deletions byterun/weak.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,14 @@ value caml_ephe_none = (value)&caml_dummy[1];
#define None_val (Val_int(0))
#define Some_tag 0

struct caml_ephe_info* caml_alloc_ephe_info ()
{
struct caml_ephe_info* e =
caml_stat_alloc (sizeof(struct caml_ephe_info));
memset (e, 0, sizeof(struct caml_ephe_info));
return e;
}

/* [len] is a value that represents a number of words (fields) */
CAMLprim value caml_ephe_create (value len)
{
Expand All @@ -50,8 +58,8 @@ CAMLprim value caml_ephe_create (value len)
if (size < CAML_EPHE_FIRST_KEY || size > Max_wosize) caml_invalid_argument ("Weak.create");
res = caml_alloc_shr (size, Abstract_tag);

Ephe_link(res) = domain_state->ephe_list_live;
domain_state->ephe_list_live = res;
Ephe_link(res) = domain_state->ephe_info->live;
domain_state->ephe_info->live = res;
Ephe_domain(res) = caml_domain_self();
for (i = CAML_EPHE_DATA_OFFSET; i < size; i++)
Op_val(res)[i] = caml_ephe_none;
Expand Down

0 comments on commit df76040

Please sign in to comment.