[sgen] Mark on dequeue, not enqueue.
authorMark Probst <mark.probst@gmail.com>
Thu, 14 Aug 2014 22:33:40 +0000 (15:33 -0700)
committerMark Probst <mark.probst@gmail.com>
Wed, 26 Nov 2014 18:38:35 +0000 (10:38 -0800)
This is another experiment.

When marking on enqueue, what we need to do is look at the object
we're about to enqueue, to check whether it's a small or large object.
Then, when we dequeue to scan, we look at the object again, so we
essentially fetch each live object header twice.

With mark on dequeue, when we encounter a pointer we always enqueue
it, without even looking at the object.  Only when we dequeue do we
figure out whether it's marked yet, and thus whether we need to scan
it.

mono/metadata/sgen-copy-object.h
mono/metadata/sgen-gc.c
mono/metadata/sgen-gray.h
mono/metadata/sgen-major-scan-object.h
mono/metadata/sgen-marksweep.c
mono/metadata/sgen-minor-scan-object.h

index 21331c18f0605a54fab15a1215c080eb6184a74c..b1277e776155b40d6ed3d8ddc08e9609b9608e7b 100644 (file)
@@ -31,6 +31,8 @@ extern long long stat_nursery_copy_object_failed_pinned;
 extern long long stat_slots_allocated_in_vain;
 
 /*
+ * Copies an object and enqueues it if a queue is given.
+ *
  * This function can be used even if the vtable of obj is not valid
  * anymore, which is the case in the parallel collector.
  */
@@ -77,15 +79,17 @@ copy_object_no_checks (void *obj, SgenGrayQueue *queue)
        MonoVTable *vt = ((MonoObject*)obj)->vtable;
        gboolean has_references = SGEN_VTABLE_HAS_REFERENCES (vt);
        mword objsize = SGEN_ALIGN_UP (sgen_par_object_get_size (vt, (MonoObject*)obj));
+       /* FIXME: Does this not mark the newly allocated object? */
        char *destination = COLLECTOR_SERIAL_ALLOC_FOR_PROMOTION (vt, obj, objsize, has_references);
 
        if (G_UNLIKELY (!destination)) {
+               /* FIXME: Is this path ever tested? */
                collector_pin_object (obj, queue);
                sgen_set_pinned_from_failed_allocation (objsize);
                return obj;
        }
 
-       par_copy_object_no_checks (destination, vt, obj, objsize, has_references ? queue : NULL);
+       par_copy_object_no_checks (destination, vt, obj, objsize, queue);
        /* FIXME: mark mod union cards if necessary */
 
        /* set the forwarding pointer */
index f5e89d77b50b016f297414954e33c2d28cf98b05..6edd46484eb4e8145405db2defc8db51de538fe8 100644 (file)
@@ -2598,7 +2598,6 @@ major_copy_or_mark_from_roots (size_t *old_next_pin_slot, gboolean start_concurr
                                g_assert (finish_up_concurrent_mark);
                                continue;
                        }
-                       sgen_los_pin_object (bigobj->data);
                        if (SGEN_OBJECT_HAS_REFERENCES (bigobj->data))
                                GRAY_OBJECT_ENQUEUE (WORKERS_DISTRIBUTE_GRAY_QUEUE, bigobj->data, sgen_obj_get_descriptor (bigobj->data));
                        if (G_UNLIKELY (do_pin_stats))
index 42c3b54fdcef7b8c37de8d7bcc5fe85b37458a1d..af7b8ec5ab6f9252b849ae8ce20dd0d6b4872da8 100644 (file)
@@ -68,7 +68,7 @@ typedef enum {
 } GrayQueueSectionState;
 #endif
 
-#define SGEN_GRAY_QUEUE_HAVE_DESCRIPTORS
+//#define SGEN_GRAY_QUEUE_HAVE_DESCRIPTORS
 
 typedef struct _GrayQueueEntry GrayQueueEntry;
 struct _GrayQueueEntry {
index 14b11317096b03fce9987c4264258ad8fdfa18e3..b9331f4a3cc43f2bc1b583bb98f5ac35299a352f 100644 (file)
@@ -67,7 +67,7 @@ extern long long stat_scan_object_called_major;
        } while (0)
 
 static void
-CONCURRENT_NAME (major_scan_object) (char *start, mword desc, SgenGrayQueue *queue)
+CONCURRENT_NAME (major_scan_object_no_mark) (char *start, mword desc, SgenGrayQueue *queue)
 {
        SGEN_OBJECT_LAYOUT_STATISTICS_DECLARE_BITMAP;
 
@@ -82,6 +82,28 @@ CONCURRENT_NAME (major_scan_object) (char *start, mword desc, SgenGrayQueue *que
        HEAVY_STAT (++stat_scan_object_called_major);
 }
 
+static void
+CONCURRENT_NAME (major_scan_object) (char *start, mword desc, SgenGrayQueue *queue)
+{
+       if (!sgen_ptr_in_nursery (start)) {
+               if (SGEN_ALIGN_UP (sgen_safe_object_get_size ((MonoObject*)start)) <= SGEN_MAX_SMALL_OBJ_SIZE) {
+                       MSBlockInfo *block = MS_BLOCK_FOR_OBJ (start);
+                       int word, bit;
+                       MS_CALC_MARK_BIT (word, bit, start);
+                       if (MS_MARK_BIT (block, word, bit))
+                               return;
+                       MS_SET_MARK_BIT (block, word, bit);
+                       binary_protocol_mark (start, (gpointer)LOAD_VTABLE (start), sgen_safe_object_get_size ((MonoObject*)start));
+               } else {
+                       if (sgen_los_object_is_pinned (start))
+                               return;
+                       sgen_los_pin_object (start);
+               }
+       }
+
+       CONCURRENT_NAME (major_scan_object_no_mark) (start, desc, queue);
+}
+
 #ifdef SCAN_FOR_CONCURRENT_MARK
 #ifdef SGEN_PARALLEL_MARK
 #error concurrent and parallel mark not supported yet
index 464adac56f4f6c90e653f245350dfd52a2e3a2a5..b05cf9e4e02d6b9ac9f72fe8d844d7bbf5edd8c8 100644 (file)
@@ -818,24 +818,30 @@ major_dump_heap (FILE *heap_dump_file)
 
 #define MS_MARK_OBJECT_AND_ENQUEUE_CHECKED(obj,desc,block,queue) do {  \
                int __word, __bit;                                      \
+               SGEN_ASSERT (0, sgen_get_current_collection_generation () == GENERATION_OLD, "Can't majorly enqueue objects when doing minor collection"); \
                MS_CALC_MARK_BIT (__word, __bit, (obj));                \
-               if (!MS_MARK_BIT ((block), __word, __bit) && MS_OBJ_ALLOCED ((obj), (block))) { \
-                       MS_SET_MARK_BIT ((block), __word, __bit);       \
-                       if ((block)->has_references)                    \
+               if (MS_OBJ_ALLOCED ((obj), (block))) { \
+                       if ((block)->has_references) {                  \
                                GRAY_OBJECT_ENQUEUE ((queue), (obj), (desc)); \
-                       binary_protocol_mark ((obj), (gpointer)LOAD_VTABLE ((obj)), sgen_safe_object_get_size ((MonoObject*)(obj))); \
+                       } else {                                        \
+                               MS_SET_MARK_BIT ((block), __word, __bit); \
+                               binary_protocol_mark ((obj), (gpointer)LOAD_VTABLE ((obj)), sgen_safe_object_get_size ((MonoObject*)(obj))); \
+                       }                                               \
                        INC_NUM_MAJOR_OBJECTS_MARKED ();                \
                }                                                       \
        } while (0)
 #define MS_MARK_OBJECT_AND_ENQUEUE(obj,desc,block,queue) do {          \
                int __word, __bit;                                      \
+               SGEN_ASSERT (0, sgen_get_current_collection_generation () == GENERATION_OLD, "Can't majorly enqueue objects when doing minor collection"); \
                MS_CALC_MARK_BIT (__word, __bit, (obj));                \
                SGEN_ASSERT (9, MS_OBJ_ALLOCED ((obj), (block)), "object %p not allocated", obj);       \
-               if (!MS_MARK_BIT ((block), __word, __bit)) {            \
-                       MS_SET_MARK_BIT ((block), __word, __bit);       \
-                       if ((block)->has_references)                    \
+               {               \
+                       if ((block)->has_references) {                  \
                                GRAY_OBJECT_ENQUEUE ((queue), (obj), (desc)); \
-                       binary_protocol_mark ((obj), (gpointer)LOAD_VTABLE ((obj)), sgen_safe_object_get_size ((MonoObject*)(obj))); \
+                       } else {                                        \
+                               MS_SET_MARK_BIT ((block), __word, __bit); \
+                               binary_protocol_mark ((obj), (gpointer)LOAD_VTABLE ((obj)), sgen_safe_object_get_size ((MonoObject*)(obj))); \
+                       }                                               \
                        INC_NUM_MAJOR_OBJECTS_MARKED ();                \
                }                                                       \
        } while (0)
@@ -956,8 +962,10 @@ major_copy_or_mark_object (void **ptr, void *obj, SgenGrayQueue *queue)
                        block = MS_BLOCK_FOR_OBJ (obj);
                        MS_CALC_MARK_BIT (word, bit, obj);
                        SGEN_ASSERT (9, !MS_MARK_BIT (block, word, bit), "object %p already marked", obj);
-                       MS_SET_MARK_BIT (block, word, bit);
-                       binary_protocol_mark (obj, (gpointer)LOAD_VTABLE (obj), sgen_safe_object_get_size ((MonoObject*)obj));
+                       if (!SGEN_VTABLE_HAS_REFERENCES (LOAD_VTABLE (obj))) {
+                               MS_SET_MARK_BIT (block, word, bit);
+                               binary_protocol_mark (obj, (gpointer)LOAD_VTABLE (obj), sgen_safe_object_get_size ((MonoObject*)obj));
+                       }
                }
        } else {
                char *forwarded;
@@ -1007,7 +1015,6 @@ major_copy_or_mark_object (void **ptr, void *obj, SgenGrayQueue *queue)
                        }
 #endif
 
-                       sgen_los_pin_object (obj);
                        if (SGEN_OBJECT_HAS_REFERENCES (obj))
                                GRAY_OBJECT_ENQUEUE (queue, obj, sgen_obj_get_descriptor (obj));
                }
@@ -1073,19 +1080,13 @@ static long long stat_drain_loops;
 static gboolean
 optimized_copy_or_mark_object (void **ptr, void *obj, SgenGrayQueue *queue)
 {
-       MSBlockInfo *block;
-       mword vtable_word = *(mword*)obj;
-
-       /* we probably only want to prefetch for major heap objects */
-       // PREFETCH ((void*)vtable_word);
-
        HEAVY_STAT (++stat_optimized_copy_object_called);
 
        SGEN_ASSERT (9, obj, "null object from pointer %p", ptr);
        SGEN_ASSERT (9, current_collection_generation == GENERATION_OLD, "old gen parallel allocator called from a %d collection", current_collection_generation);
 
        if (sgen_ptr_in_nursery (obj)) {
-               int word, bit;
+               mword vtable_word = *(mword*)obj;
                char *forwarded, *old_obj;
 
                HEAVY_STAT (++stat_optimized_nursery);
@@ -1104,78 +1105,18 @@ optimized_copy_or_mark_object (void **ptr, void *obj, SgenGrayQueue *queue)
 
                old_obj = obj;
                obj = copy_object_no_checks (obj, queue);
-               if (G_UNLIKELY (old_obj == obj)) {
-                       HEAVY_STAT (++stat_optimized_nursery_not_copied);
 
-                       /*If we fail to evacuate an object we just stop doing it for a given block size as all other will surely fail too.*/
-                       if (!sgen_ptr_in_nursery (obj)) {
-                               int size_index;
-                               block = MS_BLOCK_FOR_OBJ (obj);
-                               size_index = block->obj_size_index;
-                               evacuate_block_obj_sizes [size_index] = FALSE;
-                               MS_MARK_OBJECT_AND_ENQUEUE (obj, sgen_obj_get_descriptor (obj), block, queue);
-                               return FALSE;
-                       }
-                       return TRUE;
-               }
+               SGEN_ASSERT (0, old_obj != obj, "Cannot handle copy object failure.");
+
                *ptr = obj;
 
                HEAVY_STAT (++stat_optimized_nursery_regular);
 
-               /*
-                * FIXME: See comment for copy_object_no_checks().  If
-                * we have that, we can let the allocation function
-                * give us the block info, too, and we won't have to
-                * re-fetch it.
-                *
-                * FIXME (2): We should rework this to avoid all those nursery checks.
-                */
-               /*
-                * For the split nursery allocator the object might
-                * still be in the nursery despite having being
-                * promoted, in which case we can't mark it.
-                */
-               block = MS_BLOCK_FOR_OBJ (obj);
-               MS_CALC_MARK_BIT (word, bit, obj);
-               SGEN_ASSERT (9, !MS_MARK_BIT (block, word, bit), "object %p already marked", obj);
-               MS_SET_MARK_BIT (block, word, bit);
-               binary_protocol_mark (obj, (gpointer)LOAD_VTABLE (obj), sgen_safe_object_get_size ((MonoObject*)obj));
                return FALSE;
        } else {
-               mword desc = sgen_vtable_get_descriptor ((MonoVTable*)vtable_word);
-               int type = desc & 7;
-
-               HEAVY_STAT (++stat_optimized_major);
-
-               if (type == DESC_TYPE_SMALL_BITMAP) {
-                       int __word, __bit;
-
-                       HEAVY_STAT (++stat_optimized_major_small_fast);
-
-                       block = MS_BLOCK_FOR_OBJ (obj);
-                       MS_CALC_MARK_BIT (__word, __bit, (obj));
-                       if (!MS_MARK_BIT ((block), __word, __bit)) {
-                               MS_SET_MARK_BIT ((block), __word, __bit);
-                               GRAY_OBJECT_ENQUEUE ((queue), (obj), (desc));
-                       }
-               } else if (SGEN_ALIGN_UP (sgen_safe_object_get_size ((MonoObject*)obj)) <= SGEN_MAX_SMALL_OBJ_SIZE ) {
-                       HEAVY_STAT (++stat_optimized_major_small_slow);
-
-                       block = MS_BLOCK_FOR_OBJ (obj);
-                       MS_MARK_OBJECT_AND_ENQUEUE (obj, desc, block, queue);
-               } else {
-                       HEAVY_STAT (++stat_optimized_major_large);
-
-                       if (sgen_los_object_is_pinned (obj))
-                               return FALSE;
-                       binary_protocol_pin (obj, (gpointer)SGEN_LOAD_VTABLE (obj), sgen_safe_object_get_size ((MonoObject*)obj));
-
-                       sgen_los_pin_object (obj);
-                       if (SGEN_OBJECT_HAS_REFERENCES (obj))
-                               GRAY_OBJECT_ENQUEUE (queue, obj, sgen_obj_get_descriptor (obj));
-               }
-               return FALSE;
+               GRAY_OBJECT_ENQUEUE (queue, obj, 0);
        }
+       return FALSE;
 }
 
 static gboolean
@@ -1216,13 +1157,36 @@ drain_gray_stack (ScanCopyContext ctx)
                        return TRUE;
 #endif
 
-#ifndef SGEN_GRAY_QUEUE_HAVE_DESCRIPTORS
                desc = sgen_obj_get_descriptor_safe (obj);
-#endif
+               type = desc & 7;
+
+               HEAVY_STAT (++stat_optimized_major);
+
+               /* Mark object or, if already marked, don't process. */
+               if (!sgen_ptr_in_nursery (obj)) {
+                       if (type == DESC_TYPE_SMALL_BITMAP || SGEN_ALIGN_UP (sgen_safe_object_get_size ((MonoObject*)obj)) <= SGEN_MAX_SMALL_OBJ_SIZE) {
+                               MSBlockInfo *block = MS_BLOCK_FOR_OBJ (obj);
+                               int __word, __bit;
+
+                               HEAVY_STAT (++stat_optimized_major_small_fast);
+
+                               MS_CALC_MARK_BIT (__word, __bit, (obj));
+                               if (MS_MARK_BIT ((block), __word, __bit))
+                                       continue;
+                               MS_SET_MARK_BIT ((block), __word, __bit);
+                       } else {
+                               HEAVY_STAT (++stat_optimized_major_large);
+
+                               if (sgen_los_object_is_pinned (obj))
+                                       continue;
+                               sgen_los_pin_object (obj);
+                       }
+               }
+
+               /* Now scan the object. */
 #ifdef HEAVY_STATISTICS
                sgen_descriptor_count_scanned_object (desc);
 #endif
-               type = desc & 7;
                if (type == DESC_TYPE_SMALL_BITMAP) {
                        void **_objptr = (void**)(obj);
                        gsize _bmap = (desc) >> 16;
@@ -1234,8 +1198,7 @@ drain_gray_stack (ScanCopyContext ctx)
 
                                void *__old = *(_objptr);
                                if (__old) {
-                                       gboolean still_in_nursery;
-                                       still_in_nursery = optimized_copy_or_mark_object (_objptr, __old, queue);
+                                       gboolean still_in_nursery = optimized_copy_or_mark_object (_objptr, __old, queue);
                                        if (G_UNLIKELY (still_in_nursery && !sgen_ptr_in_nursery ((_objptr)))) {
                                                void *__copy = *(_objptr);
                                                sgen_add_to_global_remset ((_objptr), __copy);
@@ -1245,7 +1208,7 @@ drain_gray_stack (ScanCopyContext ctx)
                                _objptr ++;
                        } while (_bmap);
                } else {
-                       major_scan_object (obj, desc, queue);
+                       major_scan_object_no_mark (obj, desc, queue);
                }
        }
 }
@@ -1479,6 +1442,7 @@ ms_sweep (void)
        for (i = 0; i < num_block_obj_sizes; ++i) {
                float usage = (float)slots_used [i] / (float)slots_available [i];
                if (num_blocks [i] > 5 && usage < evacuation_threshold) {
+                       g_assert_not_reached ();
                        evacuate_block_obj_sizes [i] = TRUE;
                        /*
                        g_print ("slot size %d - %d of %d used\n",
index 3cff269a7da3d5bc65ceedcb658fe34461b2ad57..cce3d5c8576e948a5a35eaf47ce145e25b851baf 100644 (file)
@@ -53,6 +53,8 @@ SERIAL_SCAN_OBJECT (char *start, mword desc, SgenGrayQueue *queue)
        sgen_descriptor_count_scanned_object (desc);
 #endif
 
+       SGEN_ASSERT (0, sgen_get_current_collection_generation () == GENERATION_NURSERY, "Must not use this to scan during major collection.");
+
 #define SCAN_OBJECT_PROTOCOL
 #include "sgen-scan-object.h"