2010-05-21 Zoltan Varga <vargaz@gmail.com>
[mono.git] / mono / metadata / sgen-gc.c
index 1a18d1a9a0c48a0c4f7c6ebff53107da44d2c172..1852431527c2b408e6b7be963d91318489f1fdb1 100644 (file)
@@ -253,31 +253,32 @@ static gboolean do_scan_starts_check = FALSE;
 #endif
 
 #ifdef HEAVY_STATISTICS
-static long stat_objects_alloced = 0;
-static long stat_bytes_alloced = 0;
-static long stat_objects_alloced_degraded = 0;
-static long stat_bytes_alloced_degraded = 0;
-static long stat_bytes_alloced_los = 0;
-
-static long stat_copy_object_called_nursery = 0;
-static long stat_objects_copied_nursery = 0;
-static long stat_copy_object_called_major = 0;
-static long stat_objects_copied_major = 0;
-
-static long stat_scan_object_called_nursery = 0;
-static long stat_scan_object_called_major = 0;
-
-static long stat_nursery_copy_object_failed_from_space = 0;
-static long stat_nursery_copy_object_failed_forwarded = 0;
-static long stat_nursery_copy_object_failed_pinned = 0;
-
-static long stat_store_remsets = 0;
-static long stat_store_remsets_unique = 0;
-static long stat_saved_remsets_1 = 0;
-static long stat_saved_remsets_2 = 0;
-static long stat_global_remsets_added = 0;
-static long stat_global_remsets_readded = 0;
-static long stat_global_remsets_processed = 0;
+static long long stat_objects_alloced = 0;
+static long long stat_bytes_alloced = 0;
+static long long stat_objects_alloced_degraded = 0;
+static long long stat_bytes_alloced_degraded = 0;
+static long long stat_bytes_alloced_los = 0;
+
+static long long stat_copy_object_called_nursery = 0;
+static long long stat_objects_copied_nursery = 0;
+static long long stat_copy_object_called_major = 0;
+static long long stat_objects_copied_major = 0;
+
+static long long stat_scan_object_called_nursery = 0;
+static long long stat_scan_object_called_major = 0;
+
+static long long stat_nursery_copy_object_failed_from_space = 0;
+static long long stat_nursery_copy_object_failed_forwarded = 0;
+static long long stat_nursery_copy_object_failed_pinned = 0;
+
+static long long stat_store_remsets = 0;
+static long long stat_store_remsets_unique = 0;
+static long long stat_saved_remsets_1 = 0;
+static long long stat_saved_remsets_2 = 0;
+static long long stat_global_remsets_added = 0;
+static long long stat_global_remsets_readded = 0;
+static long long stat_global_remsets_processed = 0;
+static long long stat_global_remsets_discarded = 0;
 
 static int stat_wbarrier_set_field = 0;
 static int stat_wbarrier_set_arrayref = 0;
@@ -289,29 +290,29 @@ static int stat_wbarrier_value_copy = 0;
 static int stat_wbarrier_object_copy = 0;
 #endif
 
-static long time_minor_pre_collection_fragment_clear = 0;
-static long time_minor_pinning = 0;
-static long time_minor_scan_remsets = 0;
-static long time_minor_scan_pinned = 0;
-static long time_minor_scan_registered_roots = 0;
-static long time_minor_scan_thread_data = 0;
-static long time_minor_finish_gray_stack = 0;
-static long time_minor_fragment_creation = 0;
-
-static long time_major_pre_collection_fragment_clear = 0;
-static long time_major_pinning = 0;
-static long time_major_scan_pinned = 0;
-static long time_major_scan_registered_roots = 0;
-static long time_major_scan_thread_data = 0;
-static long time_major_scan_alloc_pinned = 0;
-static long time_major_scan_finalized = 0;
-static long time_major_scan_big_objects = 0;
-static long time_major_finish_gray_stack = 0;
-static long time_major_sweep = 0;
-static long time_major_fragment_creation = 0;
-
-static long pinned_chunk_bytes_alloced = 0;
-static long large_internal_bytes_alloced = 0;
+static long long time_minor_pre_collection_fragment_clear = 0;
+static long long time_minor_pinning = 0;
+static long long time_minor_scan_remsets = 0;
+static long long time_minor_scan_pinned = 0;
+static long long time_minor_scan_registered_roots = 0;
+static long long time_minor_scan_thread_data = 0;
+static long long time_minor_finish_gray_stack = 0;
+static long long time_minor_fragment_creation = 0;
+
+static long long time_major_pre_collection_fragment_clear = 0;
+static long long time_major_pinning = 0;
+static long long time_major_scan_pinned = 0;
+static long long time_major_scan_registered_roots = 0;
+static long long time_major_scan_thread_data = 0;
+static long long time_major_scan_alloc_pinned = 0;
+static long long time_major_scan_finalized = 0;
+static long long time_major_scan_big_objects = 0;
+static long long time_major_finish_gray_stack = 0;
+static long long time_major_sweep = 0;
+static long long time_major_fragment_creation = 0;
+
+static long long pinned_chunk_bytes_alloced = 0;
+static long long large_internal_bytes_alloced = 0;
 
 /* Keep in sync with internal_mem_names in dump_heap()! */
 enum {
@@ -561,6 +562,9 @@ static RememberedSet *global_remset;
 static RememberedSet *freed_thread_remsets;
 static GenericStoreRememberedSet *generic_store_remsets = NULL;
 
+/*A two slots cache for recently inserted remsets */
+static gpointer global_remset_cache [2];
+
 /* FIXME: later choose a size that takes into account the RememberedSet struct
  * and doesn't waste any alloc paddin space.
  */
@@ -628,10 +632,10 @@ safe_object_get_size (MonoObject* o)
 {
        MonoClass *klass = ((MonoVTable*)LOAD_VTABLE (o))->klass;
        if (klass == mono_defaults.string_class) {
-               return sizeof (MonoString) + 2 * mono_string_length ((MonoString*) o) + 2;
+               return sizeof (MonoString) + 2 * mono_string_length_fast ((MonoString*) o) + 2;
        } else if (klass->rank) {
                MonoArray *array = (MonoArray*)o;
-               size_t size = sizeof (MonoArray) + klass->sizes.element_size * mono_array_length (array);
+               size_t size = sizeof (MonoArray) + klass->sizes.element_size * mono_array_length_fast (array);
                if (G_UNLIKELY (array->bounds)) {
                        size += sizeof (mono_array_size_t) - 1;
                        size &= ~(sizeof (mono_array_size_t) - 1);
@@ -1300,7 +1304,7 @@ mono_gc_get_bitmap_for_descr (void *descr, int *numbits)
 
 /* helper macros to scan and traverse objects, macros because we resue them in many functions */
 #define STRING_SIZE(size,str) do {     \
-               (size) = sizeof (MonoString) + 2 * mono_string_length ((MonoString*)(str)) + 2; \
+               (size) = sizeof (MonoString) + 2 * mono_string_length_fast ((MonoString*)(str)) + 2;    \
                (size) += (ALLOC_ALIGN - 1);    \
                (size) &= ~(ALLOC_ALIGN - 1);   \
        } while (0)
@@ -1395,7 +1399,7 @@ mono_gc_get_bitmap_for_descr (void *descr, int *numbits)
                int mbwords = (*mbitmap_data++) - 1;    \
                int el_size = mono_array_element_size (((MonoObject*)(obj))->vtable->klass);    \
                char *e_start = (char*)(obj) +  G_STRUCT_OFFSET (MonoArray, vector);    \
-               char *e_end = e_start + el_size * mono_array_length ((MonoArray*)(obj));        \
+               char *e_end = e_start + el_size * mono_array_length_fast ((MonoArray*)(obj));   \
                if (0) {        \
                        MonoObject *myobj = (MonoObject*)start; \
                        g_print ("found %d at %p (0x%zx): %s.%s\n", mbwords, (obj), (vt)->desc, myobj->vtable->klass->name_space, myobj->vtable->klass->name);  \
@@ -1429,7 +1433,7 @@ mono_gc_get_bitmap_for_descr (void *descr, int *numbits)
                        int etype = (vt)->desc & 0xc000;        \
                        if (etype == (DESC_TYPE_V_REFS << 14)) {        \
                                void **p = (void**)((char*)(obj) + G_STRUCT_OFFSET (MonoArray, vector));        \
-                               void **end_refs = (void**)((char*)p + el_size * mono_array_length ((MonoArray*)(obj))); \
+                               void **end_refs = (void**)((char*)p + el_size * mono_array_length_fast ((MonoArray*)(obj)));    \
                                /* Note: this code can handle also arrays of struct with only references in them */     \
                                while (p < end_refs) {  \
                                        HANDLE_PTR (p, (obj));  \
@@ -1439,7 +1443,7 @@ mono_gc_get_bitmap_for_descr (void *descr, int *numbits)
                                int offset = ((vt)->desc >> 16) & 0xff; \
                                int num_refs = ((vt)->desc >> 24) & 0xff;       \
                                char *e_start = (char*)(obj) + G_STRUCT_OFFSET (MonoArray, vector);     \
-                               char *e_end = e_start + el_size * mono_array_length ((MonoArray*)(obj));        \
+                               char *e_end = e_start + el_size * mono_array_length_fast ((MonoArray*)(obj));   \
                                while (e_start < e_end) {       \
                                        void **p = (void**)e_start;     \
                                        int i;  \
@@ -1451,7 +1455,7 @@ mono_gc_get_bitmap_for_descr (void *descr, int *numbits)
                                }       \
                        } else if (etype == DESC_TYPE_V_BITMAP << 14) { \
                                char *e_start = (char*)(obj) +  G_STRUCT_OFFSET (MonoArray, vector);    \
-                               char *e_end = e_start + el_size * mono_array_length ((MonoArray*)(obj));        \
+                               char *e_end = e_start + el_size * mono_array_length_fast ((MonoArray*)(obj));   \
                                while (e_start < e_end) {       \
                                        void **p = (void**)e_start;     \
                                        gsize _bmap = (vt)->desc >> 16; \
@@ -1469,8 +1473,8 @@ mono_gc_get_bitmap_for_descr (void *descr, int *numbits)
                }       \
        } while (0)
 
-//#include "sgen-major-copying.c"
-#include "sgen-marksweep.c"
+#include "sgen-major-copying.c"
+//#include "sgen-marksweep.c"
 
 static gboolean
 is_xdomain_ref_allowed (gpointer *ptr, char *obj, MonoDomain *domain)
@@ -1977,6 +1981,48 @@ mono_gc_clear_domain (MonoDomain * domain)
        UNLOCK_GC;
 }
 
+static void
+global_remset_cache_clear (void)
+{
+       memset (global_remset_cache, 0, sizeof (global_remset_cache));
+}
+
+/*
+ * Tries to check if a given remset location was already added to the global remset.
+ * It can
+ *
+ * A 2 entry, LRU cache of recently saw location remsets.
+ *
+ * It's hand-coded instead of done using loops to reduce the number of memory references on cache hit.
+ *
+ * Returns TRUE is the element was added..
+ */
+static gboolean
+global_remset_location_was_not_added (gpointer ptr)
+{
+
+       gpointer first = global_remset_cache [0], second;
+       if (first == ptr) {
+               HEAVY_STAT (++stat_global_remsets_discarded);
+               return FALSE;
+       }
+
+       second = global_remset_cache [1];
+
+       if (second == ptr) {
+               /*Move the second to the front*/
+               global_remset_cache [0] = second;
+               global_remset_cache [1] = first;
+
+               HEAVY_STAT (++stat_global_remsets_discarded);
+               return FALSE;
+       }
+
+       global_remset_cache [0] = second;
+       global_remset_cache [1] = ptr;
+       return TRUE;
+}
+
 /*
  * add_to_global_remset:
  *
@@ -1988,11 +2034,14 @@ add_to_global_remset (gpointer ptr)
 {
        RememberedSet *rs;
 
+       g_assert (!ptr_in_nursery (ptr) && ptr_in_nursery (*(gpointer*)ptr));
+
+       if (!global_remset_location_was_not_added (ptr))
+               return;
+
        DEBUG (8, fprintf (gc_debug_file, "Adding global remset for %p\n", ptr));
        binary_protocol_global_remset (ptr, *(gpointer*)ptr, (gpointer)LOAD_VTABLE (*(gpointer*)ptr));
 
-       g_assert (!ptr_in_nursery (ptr) && ptr_in_nursery (*(gpointer*)ptr));
-
        HEAVY_STAT (++stat_global_remsets_added);
 
        /* 
@@ -3074,8 +3123,8 @@ dump_heap (const char *type, int num, const char *reason)
        if (reason)
                fprintf (heap_dump_file, " reason=\"%s\"", reason);
        fprintf (heap_dump_file, ">\n");
-       fprintf (heap_dump_file, "<other-mem-usage type=\"pinned-chunks\" size=\"%ld\"/>\n", pinned_chunk_bytes_alloced);
-       fprintf (heap_dump_file, "<other-mem-usage type=\"large-internal\" size=\"%ld\"/>\n", large_internal_bytes_alloced);
+       fprintf (heap_dump_file, "<other-mem-usage type=\"pinned-chunks\" size=\"%lld\"/>\n", pinned_chunk_bytes_alloced);
+       fprintf (heap_dump_file, "<other-mem-usage type=\"large-internal\" size=\"%lld\"/>\n", large_internal_bytes_alloced);
        fprintf (heap_dump_file, "<other-mem-usage type=\"mempools\" size=\"%ld\"/>\n", mono_mempool_get_bytes_allocated ());
        for (i = 0; i < INTERNAL_MEM_MAX; ++i)
                fprintf (heap_dump_file, "<other-mem-usage type=\"%s\" size=\"%ld\"/>\n", internal_mem_names [i], small_internal_mem_bytes [i]);
@@ -3164,6 +3213,8 @@ init_stats (void)
        mono_counters_register ("Global remsets added", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_global_remsets_added);
        mono_counters_register ("Global remsets re-added", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_global_remsets_readded);
        mono_counters_register ("Global remsets processed", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_global_remsets_processed);
+       mono_counters_register ("Global remsets discarded", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_global_remsets_discarded);
+
 #endif
 
        inited = TRUE;
@@ -3220,6 +3271,9 @@ collect_nursery (size_t requested_size)
 
        num_minor_gcs++;
        mono_stats.minor_gc_count ++;
+
+       global_remset_cache_clear ();
+
        /* pin from pinned handles */
        init_pinning ();
        pin_from_roots (nursery_start, nursery_next);
@@ -3349,6 +3403,7 @@ major_do_collection (const char *reason)
         */
        /* The remsets are not useful for a major collection */
        clear_remsets ();
+       global_remset_cache_clear ();
 
        TV_GETTIME (atv);
        init_pinning ();
@@ -4485,6 +4540,7 @@ clear_unreachable_ephemerons (CopyOrMarkObjectFunc copy_func, char *start, char
        EphemeronLinkNode *current = ephemeron_list, *prev = NULL;
        MonoArray *array;
        Ephemeron *cur, *array_end;
+       char *tombstone;
 
        while (current) {
                char *object = current->array;
@@ -4516,12 +4572,13 @@ clear_unreachable_ephemerons (CopyOrMarkObjectFunc copy_func, char *start, char
 
                array = (MonoArray*)object;
                cur = mono_array_addr (array, Ephemeron, 0);
-               array_end = cur + mono_array_length (array);
+               array_end = cur + mono_array_length_fast (array);
+               tombstone = (char*)((MonoVTable*)LOAD_VTABLE (object))->domain->ephemeron_tombstone;
 
                for (; cur < array_end; ++cur) {
                        char *key = (char*)cur->key;
 
-                       if (!key)
+                       if (!key || key == tombstone)
                                continue;
 
                        DEBUG (5, fprintf (gc_debug_file, "[%d] key %p (%s) value %p (%s)\n", cur - mono_array_addr (array, Ephemeron, 0),
@@ -4529,7 +4586,7 @@ clear_unreachable_ephemerons (CopyOrMarkObjectFunc copy_func, char *start, char
                                cur->value, cur->value && object_is_reachable (cur->value, start, end) ? "reachable" : "unreachable"));
 
                        if (!object_is_reachable (key, start, end)) {
-                               cur->key = NULL;
+                               cur->key = tombstone;
                                cur->value = NULL;
                                continue;
                        }
@@ -4558,6 +4615,7 @@ mark_ephemerons_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end
        EphemeronLinkNode *current = ephemeron_list;
        MonoArray *array;
        Ephemeron *cur, *array_end;
+       char *tombstone;
 
        for (current = ephemeron_list; current; current = current->next) {
                char *object = current->array;
@@ -4577,12 +4635,13 @@ mark_ephemerons_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end
 
                array = (MonoArray*)object;
                cur = mono_array_addr (array, Ephemeron, 0);
-               array_end = cur + mono_array_length (array);
+               array_end = cur + mono_array_length_fast (array);
+               tombstone = (char*)((MonoVTable*)LOAD_VTABLE (object))->domain->ephemeron_tombstone;
 
                for (; cur < array_end; ++cur) {
                        char *key = cur->key;
 
-                       if (!key)
+                       if (!key || key == tombstone)
                                continue;
 
                        DEBUG (5, fprintf (gc_debug_file, "[%d] key %p (%s) value %p (%s)\n", cur - mono_array_addr (array, Ephemeron, 0),
@@ -5778,7 +5837,13 @@ scan_from_remsets (void *start_nursery, void *end_nursery)
                DEBUG (4, fprintf (gc_debug_file, "Scanning global remset range: %p-%p, size: %zd\n", remset->data, remset->store_next, remset->store_next - remset->data));
                store_pos = remset->data;
                for (p = remset->data; p < remset->store_next; p = next_p) {
-                       mword ptr;
+                       void **ptr = p [0];
+
+                       /*Ignore previously processed remset.*/
+                       if (!global_remset_location_was_not_added (ptr)) {
+                               next_p = p + 1;
+                               continue;
+                       }
 
                        next_p = handle_remset (p, start_nursery, end_nursery, TRUE);
 
@@ -5786,15 +5851,12 @@ scan_from_remsets (void *start_nursery, void *end_nursery)
                         * Clear global remsets of locations which no longer point to the 
                         * nursery. Otherwise, they could grow indefinitely between major 
                         * collections.
+                        *
+                        * Since all global remsets are location remsets, we don't need to unmask the pointer.
                         */
-                       ptr = (p [0] & ~REMSET_TYPE_MASK);
-                       if ((p [0] & REMSET_TYPE_MASK) == REMSET_LOCATION) {
-                               if (ptr_in_nursery (*(void**)ptr)) {
-                                       *store_pos ++ = p [0];
-                                       HEAVY_STAT (++stat_global_remsets_readded);
-                               }
-                       } else {
-                               g_assert_not_reached ();
+                       if (ptr_in_nursery (*ptr)) {
+                               *store_pos ++ = p [0];
+                               HEAVY_STAT (++stat_global_remsets_readded);
                        }
                }