[pypy-commit] stmgc c8-faster-smallobj-sync: WIP
Raemi
noreply at buildbot.pypy.org
Tue Aug 11 14:14:42 CEST 2015
Author: Remi Meier <remi.meier at inf.ethz.ch>
Branch: c8-faster-smallobj-sync
Changeset: r1931:b37387a2af20
Date: 2015-08-11 14:17 +0200
http://bitbucket.org/pypy/stmgc/changeset/b37387a2af20/
Log: WIP
diff --git a/c8/TODO b/c8/TODO
--- a/c8/TODO
+++ b/c8/TODO
@@ -23,6 +23,8 @@
- avoid __builtin_frame_address(0) in precisely the performance-critical
functions like the interpreter main loop
+- some lists in PSEGMENT can only grow and never shrink again
+ check if it matters
---------------------------
DONE:
diff --git a/c8/stm/core.c b/c8/stm/core.c
--- a/c8/stm/core.c
+++ b/c8/stm/core.c
@@ -1155,6 +1155,7 @@
assert(list_is_empty(STM_PSEGMENT->modified_old_objects));
assert(list_is_empty(STM_PSEGMENT->large_overflow_objects));
+ assert(list_is_empty(STM_PSEGMENT->small_overflow_obj_ranges));
assert(list_is_empty(STM_PSEGMENT->objects_pointing_to_nursery));
assert(list_is_empty(STM_PSEGMENT->young_weakrefs));
assert(tree_is_cleared(STM_PSEGMENT->young_outside_nursery));
@@ -1243,6 +1244,7 @@
list_clear(STM_PSEGMENT->objects_pointing_to_nursery);
list_clear(STM_PSEGMENT->old_objects_with_cards_set);
list_clear(STM_PSEGMENT->large_overflow_objects);
+ list_clear(STM_PSEGMENT->small_overflow_obj_ranges);
if (tl != NULL)
timing_event(tl, event);
@@ -1273,20 +1275,32 @@
#endif
}
-static void push_large_overflow_objects_to_other_segments(void)
+static void push_overflow_objects_to_other_segments(void)
{
- if (list_is_empty(STM_PSEGMENT->large_overflow_objects))
- return;
+ if (!list_is_empty(STM_PSEGMENT->large_overflow_objects)) {
+ acquire_privatization_lock(STM_SEGMENT->segment_num);
+ LIST_FOREACH_R(STM_PSEGMENT->large_overflow_objects, object_t *,
+ ({
+ assert(!(item->stm_flags & GCFLAG_WB_EXECUTED));
+ synchronize_object_enqueue(item);
+ }));
+ synchronize_objects_flush();
+ release_privatization_lock(STM_SEGMENT->segment_num);
+ }
- /* XXX: also pushes small ones right now */
- acquire_privatization_lock(STM_SEGMENT->segment_num);
- LIST_FOREACH_R(STM_PSEGMENT->large_overflow_objects, object_t *,
- ({
- assert(!(item->stm_flags & GCFLAG_WB_EXECUTED));
- synchronize_object_enqueue(item);
- }));
- synchronize_objects_flush();
- release_privatization_lock(STM_SEGMENT->segment_num);
+ if (!list_is_empty(STM_PSEGMENT->small_overflow_obj_ranges)) {
+ acquire_privatization_lock(STM_SEGMENT->segment_num);
+
+ struct list_s *lst = STM_PSEGMENT->small_overflow_obj_ranges;
+ while (!list_is_empty(lst)) {
+ ssize_t len = (ssize_t)list_pop_item(lst);
+ stm_char *start = (stm_char*)list_pop_item(lst);
+ _synchronize_fragment(start, len);
+ }
+
+ synchronize_objects_flush();
+ release_privatization_lock(STM_SEGMENT->segment_num);
+ }
/* we can as well clear the list here, since the
objects are only useful if the commit succeeds. And
@@ -1297,6 +1311,7 @@
unknown-to-the-segment/uncommitted things.
*/
list_clear(STM_PSEGMENT->large_overflow_objects);
+ list_clear(STM_PSEGMENT->small_overflow_obj_ranges);
}
@@ -1335,7 +1350,7 @@
s_mutex_unlock();
}
- push_large_overflow_objects_to_other_segments();
+ push_overflow_objects_to_other_segments();
/* push before validate. otherwise they are reachable too early */
@@ -1517,6 +1532,7 @@
list_clear(pseg->objects_pointing_to_nursery);
list_clear(pseg->old_objects_with_cards_set);
list_clear(pseg->large_overflow_objects);
+ list_clear(pseg->small_overflow_obj_ranges);
list_clear(pseg->young_weakrefs);
#pragma pop_macro("STM_SEGMENT")
#pragma pop_macro("STM_PSEGMENT")
@@ -1709,7 +1725,6 @@
}
-
static void synchronize_object_enqueue(object_t *obj)
{
assert(!_is_young(obj));
@@ -1785,3 +1800,41 @@
DEBUG_EXPECT_SEGFAULT(true);
}
+
+
+
+static void small_overflow_obj_ranges_add(object_t *obj)
+{
+ assert(is_small_uniform(obj));
+
+ ssize_t obj_size = stmcb_size_rounded_up(
+ (struct object_s *)REAL_ADDRESS(STM_SEGMENT->segment_base, obj));
+ OPT_ASSERT(obj_size >= 16);
+
+ struct list_s *lst = STM_PSEGMENT->small_overflow_obj_ranges;
+ if (!list_is_empty(lst)) {
+ /* try to merge with other ranges (XXX: quadratic, problem?) */
+ stm_char *obj_start = (stm_char*)obj;
+ long i;
+ for (i = lst->count - 2; i >= 0; i -= 2) {
+ stm_char *start = (stm_char*)lst->items[i];
+ ssize_t size = (ssize_t)lst->items[i+1];
+
+ if (start + size == obj_start) {
+ /* merge!
+ Note: we cannot overlap pages by adding to a range
+ in this way, since the first slot in a smallmalloc
+ page is always unused. Thus, we never merge the
+ last obj in a page with the first obj in a successor
+ page. */
+ lst->items[i+1] = size + obj_size;
+ return;
+ }
+ }
+ }
+
+ /* no merge was found */
+ STM_PSEGMENT->small_overflow_obj_ranges =
+ list_append2(STM_PSEGMENT->small_overflow_obj_ranges,
+ (uintptr_t)obj, (uintptr_t)obj_size);
+}
diff --git a/c8/stm/core.h b/c8/stm/core.h
--- a/c8/stm/core.h
+++ b/c8/stm/core.h
@@ -104,10 +104,17 @@
/* list of objects created in the current transaction and
that survived at least one minor collection. They need
to be synchronized to other segments on commit, but they
- do not need to be in the commit log entry.
- XXX: for now it also contains small overflow objs */
+ do not need to be in the commit log entry. */
struct list_s *large_overflow_objects;
+ /* lists the memory ranges of uncommitted/overflow objs that
+ need to be flushed to other segments on commit (like
+ large_overflow_objects). (unsorted, a range never overlaps
+ pages) */
+ /* XXX: not much different from before. Maybe try a ranges list
+ per size class. */
+ struct list_s *small_overflow_obj_ranges;
+
uint8_t privatization_lock; // XXX KILL
uint8_t safe_point;
@@ -303,8 +310,10 @@
static void touch_all_pages_of_obj(object_t *obj, size_t obj_size);
+static inline void _synchronize_fragment(stm_char *frag, ssize_t frag_size);
static void synchronize_object_enqueue(object_t *obj);
static void synchronize_objects_flush(void);
+static void small_overflow_obj_ranges_add(object_t *obj);
static void _signal_handler(int sig, siginfo_t *siginfo, void *context);
static bool _stm_validate(void);
diff --git a/c8/stm/forksupport.c b/c8/stm/forksupport.c
--- a/c8/stm/forksupport.c
+++ b/c8/stm/forksupport.c
@@ -98,6 +98,7 @@
STM_PSEGMENT->safe_point = SP_NO_TRANSACTION;
list_clear(STM_PSEGMENT->objects_pointing_to_nursery);
list_clear(STM_PSEGMENT->large_overflow_objects);
+ list_clear(STM_PSEGMENT->small_overflow_obj_ranges);
s_mutex_unlock();
return;
diff --git a/c8/stm/gcpage.c b/c8/stm/gcpage.c
--- a/c8/stm/gcpage.c
+++ b/c8/stm/gcpage.c
@@ -597,6 +597,9 @@
}
}
+ /* XXX: fix small_overflow_obj_ranges */
+ abort();
+
/* Remove from 'modified_old_objects' all old hashtables that die */
{
lst = pseg->modified_old_objects;
diff --git a/c8/stm/nursery.c b/c8/stm/nursery.c
--- a/c8/stm/nursery.c
+++ b/c8/stm/nursery.c
@@ -57,7 +57,7 @@
static object_t *find_existing_shadow(object_t *obj);
#define GCWORD_MOVED ((object_t *) -1)
#define FLAG_SYNC_LARGE 0x01
-
+#define FLAG_SYNC_SMALL 0x02
static void minor_trace_if_young(object_t **pobj)
{
@@ -96,6 +96,8 @@
dprintf(("has_shadow(%p): %p, sz:%lu\n",
obj, nobj, size));
+ /* XXX: shadows are always large-mallocd */
+ nobj_sync_now = FLAG_SYNC_LARGE;
goto copy_large_object;
}
@@ -106,10 +108,12 @@
/* case 1: object is not small enough.
Ask gcpage.c for an allocation via largemalloc. */
nobj = (object_t *)allocate_outside_nursery_large(size);
+ nobj_sync_now = FLAG_SYNC_LARGE;
}
else {
/* case "small enough" */
nobj = (object_t *)allocate_outside_nursery_small(size);
+ nobj_sync_now = FLAG_SYNC_SMALL;
}
//dprintf(("move %p -> %p\n", obj, nobj));
@@ -118,7 +122,7 @@
char *realnobj = REAL_ADDRESS(STM_SEGMENT->segment_base, nobj);
memcpy(realnobj, realobj, size);
- nobj_sync_now = ((uintptr_t)nobj) | FLAG_SYNC_LARGE;
+ nobj_sync_now |= (uintptr_t)nobj; /* SYNC_LARGE set above */
pforwarded_array[0] = GCWORD_MOVED;
pforwarded_array[1] = nobj;
@@ -133,6 +137,7 @@
/* a young object outside the nursery */
nobj = obj;
tree_delete_item(STM_PSEGMENT->young_outside_nursery, (uintptr_t)nobj);
+ assert(!is_small_uniform(nobj));
nobj_sync_now = ((uintptr_t)nobj) | FLAG_SYNC_LARGE;
}
@@ -363,31 +368,42 @@
while (!list_is_empty(lst)) {
uintptr_t obj_sync_now = list_pop_item(lst);
- object_t *obj = (object_t *)(obj_sync_now & ~FLAG_SYNC_LARGE);
+ object_t *obj = (object_t *)(obj_sync_now & ~(FLAG_SYNC_LARGE|FLAG_SYNC_SMALL));
assert(!_is_in_nursery(obj));
_collect_now(obj);
assert(!(obj->stm_flags & GCFLAG_CARDS_SET));
- if (obj_sync_now & FLAG_SYNC_LARGE) {
- /* XXX: SYNC_LARGE even set for small objs right now */
- /* this is a newly allocated obj in this transaction. We must
- either synchronize the object to other segments now, or
- add the object to large_overflow_objects list */
- struct stm_priv_segment_info_s *pseg = get_priv_segment(STM_SEGMENT->segment_num);
- if (pseg->minor_collect_will_commit_now) {
- acquire_privatization_lock(pseg->pub.segment_num);
- synchronize_object_enqueue(obj);
- release_privatization_lock(pseg->pub.segment_num);
- } else {
+ /* the list could have moved while appending */
+ lst = STM_PSEGMENT->objects_pointing_to_nursery;
+
+ if (!(obj_sync_now & (FLAG_SYNC_LARGE|FLAG_SYNC_SMALL)))
+ continue;
+
+ /* obj is a newly allocated obj in this transaction. We must
+ either synchronize the object to other segments now, or
+ add the object to large_overflow_objects list */
+ struct stm_priv_segment_info_s *pseg = get_priv_segment(STM_SEGMENT->segment_num);
+ if (pseg->minor_collect_will_commit_now) {
+ /* small or large obj are directly synced now */
+ acquire_privatization_lock(pseg->pub.segment_num);
+ synchronize_object_enqueue(obj);
+ release_privatization_lock(pseg->pub.segment_num);
+
+ if (obj_sync_now & FLAG_SYNC_LARGE) {
+ _cards_cleared_in_object(pseg, obj, false);
+ }
+ } else {
+ /* remember them in lists to sync on commit */
+ if (obj_sync_now & FLAG_SYNC_LARGE) {
LIST_APPEND(STM_PSEGMENT->large_overflow_objects, obj);
+ _cards_cleared_in_object(pseg, obj, false);
+ } else { // SYNC_SMALL
+ small_overflow_obj_ranges_add(obj);
}
- _cards_cleared_in_object(pseg, obj, false);
}
- /* the list could have moved while appending */
- lst = STM_PSEGMENT->objects_pointing_to_nursery;
}
/* flush all overflow objects to other segments now */
diff --git a/c8/stm/setup.c b/c8/stm/setup.c
--- a/c8/stm/setup.c
+++ b/c8/stm/setup.c
@@ -101,6 +101,7 @@
pr->pub.segment_base = segment_base;
pr->modified_old_objects = list_create();
pr->large_overflow_objects = list_create();
+ pr->small_overflow_obj_ranges = list_create();
pr->young_weakrefs = list_create();
pr->old_weakrefs = list_create();
pr->objects_pointing_to_nursery = list_create();
@@ -158,7 +159,9 @@
list_free(pr->old_objects_with_cards_set);
list_free(pr->modified_old_objects);
assert(list_is_empty(pr->large_overflow_objects));
+ assert(list_is_empty(pr->small_overflow_obj_ranges));
list_free(pr->large_overflow_objects);
+ list_free(pr->small_overflow_obj_ranges);
list_free(pr->young_weakrefs);
list_free(pr->old_weakrefs);
tree_free(pr->young_outside_nursery);
diff --git a/c8/stm/smallmalloc.h b/c8/stm/smallmalloc.h
--- a/c8/stm/smallmalloc.h
+++ b/c8/stm/smallmalloc.h
@@ -50,12 +50,6 @@
struct small_malloc_data_s {
struct small_free_loc_s *loc_free[GC_N_SMALL_REQUESTS];
- /* lists the memory ranges of uncommitted/overflow objs that
- need to be flushed to other segments on commit (like
- large_overflow_objects). (unsorted, a range never overlaps
- pages) */
- struct list_s *uncommitted_ranges;
-
/* with "uncommitted_ranges", we do the following during major GCs:
for seg in segments:
More information about the pypy-commit
mailing list