Author: Armin Rigo <ar...@tunes.org> Branch: c8-marker Changeset: r1695:a3f57a07666c Date: 2015-03-07 16:34 +0100 http://bitbucket.org/pypy/stmgc/changeset/a3f57a07666c/
Log: in-progress: import some code from c7 diff --git a/c8/stm/core.c b/c8/stm/core.c --- a/c8/stm/core.c +++ b/c8/stm/core.c @@ -528,6 +528,7 @@ STM_PSEGMENT->safe_point = SP_NO_TRANSACTION; list_clear(STM_PSEGMENT->modified_old_objects); + list_clear(STM_PSEGMENT->modified_old_objects_markers); STM_PSEGMENT->last_commit_log_entry = new; release_modification_lock(STM_SEGMENT->segment_num); } @@ -557,6 +558,7 @@ STM_PSEGMENT->transaction_state = TS_NONE; STM_PSEGMENT->safe_point = SP_NO_TRANSACTION; list_clear(STM_PSEGMENT->modified_old_objects); + list_clear(STM_PSEGMENT->modified_old_objects_markers); STM_PSEGMENT->last_commit_log_entry = new; /* do it: */ @@ -630,6 +632,7 @@ (uintptr_t)obj, /* obj */ (uintptr_t)bk_slice, /* bk_addr */ NEW_SLICE(slice_off, slice_sz)); + timing_record_write(); dprintf(("> append slice %p, off=%lu, sz=%lu\n", bk_slice, slice_off, slice_sz)); release_modification_lock(STM_SEGMENT->segment_num); @@ -1051,6 +1054,7 @@ assert(STM_PSEGMENT->safe_point == SP_NO_TRANSACTION); assert(STM_PSEGMENT->transaction_state == TS_NONE); + timing_event(tl, STM_TRANSACTION_START); STM_PSEGMENT->transaction_state = TS_REGULAR; STM_PSEGMENT->safe_point = SP_RUNNING; #ifndef NDEBUG @@ -1071,6 +1075,7 @@ } assert(list_is_empty(STM_PSEGMENT->modified_old_objects)); + assert(list_is_empty(STM_PSEGMENT->modified_old_objects_markers)); assert(list_is_empty(STM_PSEGMENT->large_overflow_objects)); assert(list_is_empty(STM_PSEGMENT->objects_pointing_to_nursery)); assert(list_is_empty(STM_PSEGMENT->young_weakrefs)); @@ -1080,6 +1085,10 @@ assert(tree_is_cleared(STM_PSEGMENT->callbacks_on_commit_and_abort[1])); assert(list_is_empty(STM_PSEGMENT->young_objects_with_light_finalizers)); assert(STM_PSEGMENT->finalizers == NULL); +#ifndef NDEBUG + /* this should not be used when objects_pointing_to_nursery == NULL */ + STM_PSEGMENT->modified_old_objects_markers_num_old = 99999999999999999L; +#endif check_nursery_at_transaction_start(); @@ -1125,7 +1134,7 @@ /************************************************************/ -static void _finish_transaction() +static void _finish_transaction(enum stm_event_e event) { stm_thread_local_t *tl = STM_SEGMENT->running_thread; @@ -1136,6 +1145,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); + timing_event(tl, event); release_thread_segment(tl); /* cannot access STM_SEGMENT or STM_PSEGMENT from here ! */ @@ -1236,7 +1246,7 @@ /* done */ stm_thread_local_t *tl = STM_SEGMENT->running_thread; - _finish_transaction(); + _finish_transaction(STM_TRANSACTION_COMMIT); /* cannot access STM_SEGMENT or STM_PSEGMENT from here ! */ s_mutex_unlock(); @@ -1369,7 +1379,7 @@ : NURSERY_END; } - _finish_transaction(); + _finish_transaction(STM_TRANSACTION_ABORT); /* cannot access STM_SEGMENT or STM_PSEGMENT from here ! */ return tl; diff --git a/c8/stm/core.h b/c8/stm/core.h --- a/c8/stm/core.h +++ b/c8/stm/core.h @@ -93,6 +93,12 @@ struct tree_s *young_outside_nursery; struct tree_s *nursery_objects_shadows; + /* For each entry in 'modified_old_objects', we have two entries + in the following list, which give the marker at the time we added + the entry to modified_old_objects. */ + struct list_s *modified_old_objects_markers; + uintptr_t modified_old_objects_markers_num_old; + /* List of all young weakrefs to check in minor collections. These are the only weakrefs that may point to young objects and never contain NULL. */ diff --git a/c8/stm/gcpage.c b/c8/stm/gcpage.c --- a/c8/stm/gcpage.c +++ b/c8/stm/gcpage.c @@ -386,6 +386,20 @@ LIST_FREE(uniques); } +static void mark_visit_from_markers(void) +{ + long j; + for (j = 1; j < NB_SEGMENTS; j++) { + struct stm_priv_segment_info_s *pseg = get_priv_segment(j); + struct list_s *lst = pseg->modified_old_objects_markers; + uintptr_t i; + for (i = list_count(lst); i > 0; i -= 2) { + mark_visit_possibly_new_object((object_t *)list_item(lst, i - 1), + pseg); + } + } +} + static void mark_visit_from_roots(void) { if (testing_prebuilt_objs != NULL) { @@ -683,6 +697,7 @@ /* marking */ LIST_CREATE(marked_objects_to_trace); mark_visit_from_modified_objects(); + mark_visit_from_markers(); mark_visit_from_roots(); mark_visit_from_finalizer_pending(); diff --git a/c7/stm/marker.c b/c8/stm/marker.c copy from c7/stm/marker.c copy to c8/stm/marker.c --- a/c7/stm/marker.c +++ b/c8/stm/marker.c @@ -31,15 +31,6 @@ } } -static void _timing_fetch_inev(void) -{ - stm_loc_marker_t marker; - marker.tl = STM_SEGMENT->running_thread; - marker_fetch(&marker); - STM_PSEGMENT->marker_inev.odd_number = marker.odd_number; - STM_PSEGMENT->marker_inev.object = marker.object; -} - static void marker_fetch_obj_write(object_t *obj, stm_loc_marker_t *out_marker) { /* From 'out_marker->tl', fill in 'out_marker->segment_base' and @@ -48,23 +39,16 @@ */ assert(_has_mutex()); - /* here, we acquired the other thread's marker_lock, which means that: - - (1) it has finished filling 'modified_old_objects' after it sets - up the write_locks[] value that we're conflicting with - - (2) it is not mutating 'modified_old_objects' right now (we have - the global mutex_lock at this point too). - */ - long i; + long i, num; int in_segment_num = out_marker->tl->associated_segment_num; assert(in_segment_num >= 1); struct stm_priv_segment_info_s *pseg = get_priv_segment(in_segment_num); struct list_s *mlst = pseg->modified_old_objects; struct list_s *mlstm = pseg->modified_old_objects_markers; - assert(list_count(mlstm) <= 2 * list_count(mlst)); - for (i = list_count(mlstm) / 2; --i >= 0; ) { - if (list_item(mlst, i) == (uintptr_t)obj) { + num = list_count(mlstm) / 2; + assert(num * 3 <= list_count(mlst)); + for (i = 0; i < num; i++) { + if (list_item(mlst, i * 3) == (uintptr_t)obj) { out_marker->odd_number = list_item(mlstm, i * 2 + 0); out_marker->object = (object_t *)list_item(mlstm, i * 2 + 1); return; @@ -80,7 +64,7 @@ marker.tl = STM_SEGMENT->running_thread; marker_fetch(&marker); - long base_count = list_count(STM_PSEGMENT->modified_old_objects); + long base_count = list_count(STM_PSEGMENT->modified_old_objects) / 3; struct list_s *mlstm = STM_PSEGMENT->modified_old_objects_markers; while (list_count(mlstm) < 2 * base_count) { mlstm = list_append2(mlstm, 0, 0); @@ -89,55 +73,21 @@ STM_PSEGMENT->modified_old_objects_markers = mlstm; } -static void _timing_contention(enum stm_event_e kind, - uint8_t other_segment_num, object_t *obj) +static void timing_write_read_contention(object_t *obj) { - struct stm_priv_segment_info_s *other_pseg; - other_pseg = get_priv_segment(other_segment_num); + if (stmcb_timing_event == NULL) + return; - char *other_segment_base = other_pseg->pub.segment_base; - acquire_marker_lock(other_segment_base); + /* Collect the older location of the write from the current thread. */ + stm_loc_marker_t marker; + marker.tl = STM_SEGMENT->running_thread; + marker.segment_base = STM_SEGMENT->segment_base; + marker_fetch_obj_write(obj, &marker); - stm_loc_marker_t markers[2]; - - /* Collect the location for myself. It's usually the current - location, except in a write-read abort, in which case it's the - older location of the write. */ - markers[0].tl = STM_SEGMENT->running_thread; - markers[0].segment_base = STM_SEGMENT->segment_base; - - if (kind == STM_CONTENTION_WRITE_READ) - marker_fetch_obj_write(obj, &markers[0]); - else - marker_fetch(&markers[0]); - - /* For some categories, we can also collect the relevant information - for the other segment. */ - markers[1].tl = other_pseg->pub.running_thread; - markers[1].segment_base = other_pseg->pub.segment_base; - - switch (kind) { - case STM_CONTENTION_WRITE_WRITE: - marker_fetch_obj_write(obj, &markers[1]); - break; - case STM_CONTENTION_INEVITABLE: - markers[1].odd_number = other_pseg->marker_inev.odd_number; - markers[1].object = other_pseg->marker_inev.object; - break; - default: - markers[1].odd_number = 0; - markers[1].object = NULL; - break; - } - - stmcb_timing_event(markers[0].tl, kind, markers); - - /* only release the lock after stmcb_timing_event(), otherwise it could - run into race conditions trying to interpret 'markers[1].object' */ - release_marker_lock(other_segment_base); + stmcb_timing_event(marker.tl, STM_CONTENTION_WRITE_READ, &marker); } void (*stmcb_timing_event)(stm_thread_local_t *tl, /* the local thread */ enum stm_event_e event, - stm_loc_marker_t *markers); + stm_loc_marker_t *marker); diff --git a/c7/stm/marker.h b/c8/stm/marker.h copy from c7/stm/marker.h copy to c8/stm/marker.h --- a/c7/stm/marker.h +++ b/c8/stm/marker.h @@ -1,8 +1,6 @@ static void _timing_record_write(void); -static void _timing_fetch_inev(void); -static void _timing_contention(enum stm_event_e kind, - uint8_t other_segment_num, object_t *obj); +static void timing_write_read_contention(object_t *obj); #define timing_event(tl, event) \ @@ -10,10 +8,3 @@ #define timing_record_write() \ (stmcb_timing_event != NULL ? _timing_record_write() : (void)0) - -#define timing_fetch_inev() \ - (stmcb_timing_event != NULL ? _timing_fetch_inev() : (void)0) - -#define timing_contention(kind, other_segnum, obj) \ - (stmcb_timing_event != NULL ? \ - _timing_contention(kind, other_segnum, obj) : (void)0) diff --git a/c8/stm/nursery.c b/c8/stm/nursery.c --- a/c8/stm/nursery.c +++ b/c8/stm/nursery.c @@ -410,6 +410,18 @@ } } +static void collect_roots_from_markers(uintptr_t num_old) +{ + dprintf(("collect_roots_from_markers\n")); + /* visit the marker objects */ + struct list_s *mlst = STM_PSEGMENT->modified_old_objects_markers; + STM_PSEGMENT->modified_old_objects_markers_num_old = list_count(mlst); + uintptr_t i, total = list_count(mlst); + assert((total & 1) == 0); + for (i = num_old + 1; i < total; i += 2) { + minor_trace_if_young((object_t **)list_ptr_to_item(mlst, i)); + } +} static void collect_objs_still_young_but_with_finalizers(void) { @@ -494,6 +506,13 @@ dprintf(("minor_collection commit=%d\n", (int)commit)); STM_PSEGMENT->minor_collect_will_commit_now = commit; + + uintptr_t num_old; + if (STM_PSEGMENT->overflow_number_has_been_used) + num_old = STM_PSEGMENT->modified_old_objects_markers_num_old; + else + num_old = 0; + if (!commit) { /* 'STM_PSEGMENT->overflow_number' is used now by this collection, in the sense that it's copied to the overflow objects */ @@ -502,6 +521,8 @@ collect_cardrefs_to_nursery(); + collect_roots_from_markers(num_old); + collect_roots_in_nursery(); if (STM_PSEGMENT->finalizers != NULL) diff --git a/c8/stm/setup.c b/c8/stm/setup.c --- a/c8/stm/setup.c +++ b/c8/stm/setup.c @@ -100,6 +100,7 @@ pr->pub.segment_num = i; pr->pub.segment_base = segment_base; pr->modified_old_objects = list_create(); + pr->modified_old_objects_markers = list_create(); pr->large_overflow_objects = list_create(); pr->young_weakrefs = list_create(); pr->old_weakrefs = list_create(); @@ -152,6 +153,7 @@ list_free(pr->objects_pointing_to_nursery); list_free(pr->old_objects_with_cards_set); list_free(pr->modified_old_objects); + list_free(pr->modified_old_objects_markers); assert(list_is_empty(pr->large_overflow_objects)); list_free(pr->large_overflow_objects); list_free(pr->young_weakrefs); diff --git a/c8/stmgc.c b/c8/stmgc.c --- a/c8/stmgc.c +++ b/c8/stmgc.c @@ -14,6 +14,7 @@ #include "stm/gcpage.h" #include "stm/extra.h" #include "stm/fprintcolor.h" +#include "stm/marker.h" #include "stm/rewind_setjmp.h" #include "stm/finalizer.h" @@ -34,5 +35,6 @@ #include "stm/core.c" #include "stm/extra.c" #include "stm/fprintcolor.c" +#include "stm/marker.c" #include "stm/rewind_setjmp.c" #include "stm/finalizer.c" diff --git a/c8/stmgc.h b/c8/stmgc.h --- a/c8/stmgc.h +++ b/c8/stmgc.h @@ -342,6 +342,69 @@ void stm_resume_all_other_threads(void); +/* Profiling events. In the comments: content of the markers, if any */ +enum stm_event_e { + /* always STM_TRANSACTION_START followed later by one of COMMIT or ABORT */ + STM_TRANSACTION_START, + STM_TRANSACTION_COMMIT, + STM_TRANSACTION_ABORT, + + /* write-read contention: a "marker" is included in the PYPYSTM file + saying where the write was done. Followed by STM_TRANSACTION_ABORT. */ + STM_CONTENTION_WRITE_READ, + + /* always one STM_WAIT_xxx followed later by STM_WAIT_DONE */ + STM_WAIT_FREE_SEGMENT, + STM_WAIT_OTHER_INEVITABLE, + STM_WAIT_DONE, + + /* start and end of GC cycles */ + STM_GC_MINOR_START, + STM_GC_MINOR_DONE, + STM_GC_MAJOR_START, + STM_GC_MAJOR_DONE, + + _STM_EVENT_N +}; + +#define STM_EVENT_NAMES \ + "transaction start", \ + "transaction commit", \ + "transaction abort", \ + "contention write read", \ + "wait free segment", \ + "wait other inevitable", \ + "wait done", \ + "gc minor start", \ + "gc minor done", \ + "gc major start", \ + "gc major done" + +/* The markers pushed in the shadowstack are an odd number followed by a + regular pointer. */ +typedef struct { + stm_thread_local_t *tl; + char *segment_base; /* base to interpret the 'object' below */ + uintptr_t odd_number; /* marker odd number, or 0 if marker is missing */ + object_t *object; /* marker object, or NULL if marker is missing */ +} stm_loc_marker_t; +extern void (*stmcb_timing_event)(stm_thread_local_t *tl, /* the local thread */ + enum stm_event_e event, + stm_loc_marker_t *marker); + +/* Calling this sets up a stmcb_timing_event callback that will produce + a binary file called 'profiling_file_name'. Call it with + 'fork_mode == 0' for only the main process, and with + 'fork_mode == 1' to also write files called + 'profiling_file_name.fork<PID>' after a fork(). Call it with NULL to + stop profiling. Returns -1 in case of error (see errno then). + The optional 'expand_marker' function pointer is called to expand + the marker's odd_number and object into data, starting at the given + position and with the given maximum length. */ +int stm_set_timing_log(const char *profiling_file_name, int fork_mode, + int expand_marker(stm_loc_marker_t *, char *, int)); + + /* Convenience macros to push the markers into the shadowstack */ #define STM_PUSH_MARKER(tl, odd_num, p) do { \ uintptr_t _odd_num = (odd_num); \ diff --git a/c8/test/support.py b/c8/test/support.py --- a/c8/test/support.py +++ b/c8/test/support.py @@ -124,6 +124,46 @@ long stm_call_on_abort(stm_thread_local_t *, void *key, void callback(void *)); long stm_call_on_commit(stm_thread_local_t *, void *key, void callback(void *)); +/* Profiling events. In the comments: content of the markers, if any */ +enum stm_event_e { + /* always STM_TRANSACTION_START followed later by one of COMMIT or ABORT */ + STM_TRANSACTION_START, + STM_TRANSACTION_COMMIT, + STM_TRANSACTION_ABORT, + + /* write-read contention: a "marker" is included in the PYPYSTM file + saying where the write was done. Followed by STM_TRANSACTION_ABORT. */ + STM_CONTENTION_WRITE_READ, + + /* always one STM_WAIT_xxx followed later by STM_WAIT_DONE */ + STM_WAIT_FREE_SEGMENT, + STM_WAIT_OTHER_INEVITABLE, + STM_WAIT_DONE, + + /* start and end of GC cycles */ + STM_GC_MINOR_START, + STM_GC_MINOR_DONE, + STM_GC_MAJOR_START, + STM_GC_MAJOR_DONE, + ... +}; + +typedef struct { + stm_thread_local_t *tl; + /* If segment_base==NULL, the remaining fields are undefined. If non-NULL, + the rest is a marker to interpret from this segment_base addr. */ + char *segment_base; + uintptr_t odd_number; + object_t *object; +} stm_loc_marker_t; + +typedef void (*stmcb_timing_event_fn)(stm_thread_local_t *tl, + enum stm_event_e event, + stm_loc_marker_t *markers); +stmcb_timing_event_fn stmcb_timing_event; + +int stm_set_timing_log(const char *profiling_file_name, int prof_mode, + int expand_marker(stm_loc_marker_t *, char *, int)); long _stm_count_modified_old_objects(void); long _stm_count_objects_pointing_to_nursery(void); diff --git a/c7/test/test_marker.py b/c8/test/test_marker.py copy from c7/test/test_marker.py copy to c8/test/test_marker.py _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit