[sgen] Dynamic nursery sizing
[mono.git] / mono / sgen / sgen-nursery-allocator.c
index 2c192f0e904ab298cbe3b6effa7919badd391aa3..1452b6b7d79b5e995472a55cc1e1a179b9ec525d 100644 (file)
@@ -66,16 +66,21 @@ static SgenFragmentAllocator mutator_allocator;
 /* freeelist of fragment structures */
 static SgenFragment *fragment_freelist = NULL;
 
-/* Allocator cursors */
-static char *nursery_last_pinned_end = NULL;
-
 char *sgen_nursery_start;
 char *sgen_nursery_end;
 
 /* good sizes are 512KB-1MB: larger ones increase a lot memzeroing time */
-size_t sgen_nursery_size = (1 << 22);
-/* The number of trailing 0 bits in sgen_nursery_size */
-int sgen_nursery_bits = 22;
+size_t sgen_nursery_size;
+/*
+ * Maximum size that we can resize the nursery to.
+ * If sgen_nursery_default_size == sgen_nursery_max_size then we are not
+ * dynamically resizing the nursery
+ */
+size_t sgen_nursery_max_size;
+size_t sgen_nursery_min_size;
+/* The number of trailing 0 bits in sgen_nursery_max_size */
+int sgen_nursery_bits;
+
 
 char *sgen_space_bitmap;
 size_t sgen_space_bitmap_size;
@@ -345,7 +350,7 @@ par_alloc_from_fragment (SgenFragmentAllocator *allocator, SgenFragment *frag, s
        char *p = frag->fragment_next;
        char *end = p + size;
 
-       if (end > frag->fragment_end)
+       if (end > frag->fragment_end || end > (sgen_nursery_start + sgen_nursery_size))
                return NULL;
 
        /* p = frag->fragment_next must happen before */
@@ -439,9 +444,14 @@ sgen_fragment_allocator_par_alloc (SgenFragmentAllocator *allocator, size_t size
 
 restart:
        for (frag = (SgenFragment *)unmask (allocator->alloc_head); unmask (frag); frag = (SgenFragment *)unmask (frag->next)) {
+               size_t frag_size = frag->fragment_end - frag->fragment_next;
+
+               if (frag->fragment_next >= (sgen_nursery_start + sgen_nursery_size))
+                       continue;
+
                HEAVY_STAT (++stat_alloc_iterations);
 
-               if (size <= (size_t)(frag->fragment_end - frag->fragment_next)) {
+               if (size <= frag_size) {
                        void *p = par_alloc_from_fragment (allocator, frag, size);
                        if (!p) {
                                HEAVY_STAT (++stat_alloc_retries);
@@ -524,6 +534,9 @@ restart:
        for (frag = (SgenFragment *)unmask (allocator->alloc_head); frag; frag = (SgenFragment *)unmask (frag->next)) {
                size_t frag_size = frag->fragment_end - frag->fragment_next;
 
+               if (frag->fragment_next >= (sgen_nursery_start + sgen_nursery_size))
+                       continue;
+
                HEAVY_STAT (++stat_alloc_range_iterations);
 
                if (desired_size <= frag_size) {
@@ -551,9 +564,8 @@ restart:
 
        if (min_frag) {
                void *p;
-               size_t frag_size;
+               size_t frag_size = min_frag->fragment_end - min_frag->fragment_next;
 
-               frag_size = min_frag->fragment_end - min_frag->fragment_next;
                if (frag_size < minimum_size)
                        goto restart;
 
@@ -678,6 +690,26 @@ fragment_list_reverse (SgenFragmentAllocator *allocator)
        allocator->region_head = allocator->alloc_head = prev;
 }
 
+/*
+ * We split fragments at the border of the current nursery limit. When we
+ * allocate from the nursery we only consider fragments that start in the
+ * current nursery section. We build fragments for the entire nursery in
+ * order to facilitate scanning it for objects (adding a nursery frag also
+ * marks a region in the nursery as being free)
+ */
+static void
+add_nursery_frag_checks (SgenFragmentAllocator *allocator, char *frag_start, char *frag_end)
+{
+       char *nursery_limit = sgen_nursery_start + sgen_nursery_size;
+
+       if (frag_start < nursery_limit && frag_end > nursery_limit) {
+               add_nursery_frag (allocator, nursery_limit - frag_start, frag_start, nursery_limit);
+               add_nursery_frag (allocator, frag_end - nursery_limit, nursery_limit, frag_end);
+       } else {
+               add_nursery_frag (allocator, frag_end - frag_start, frag_start, frag_end);
+       }
+}
+
 mword
 sgen_build_nursery_fragments (GCMemSection *nursery_section, SgenGrayQueue *unpin_queue)
 {
@@ -738,7 +770,7 @@ sgen_build_nursery_fragments (GCMemSection *nursery_section, SgenGrayQueue *unpi
                g_assert (frag_size >= 0);
                g_assert (size > 0);
                if (frag_size && size)
-                       add_nursery_frag (&mutator_allocator, frag_size, frag_start, frag_end); 
+                       add_nursery_frag_checks (&mutator_allocator, frag_start, frag_end);
 
                frag_size = size;
 #ifdef NALLOC_DEBUG
@@ -747,11 +779,10 @@ sgen_build_nursery_fragments (GCMemSection *nursery_section, SgenGrayQueue *unpi
                frag_start = frag_end + frag_size;
        }
 
-       nursery_last_pinned_end = frag_start;
        frag_end = sgen_nursery_end;
        frag_size = frag_end - frag_start;
        if (frag_size)
-               add_nursery_frag (&mutator_allocator, frag_size, frag_start, frag_end);
+               add_nursery_frag_checks (&mutator_allocator, frag_start, frag_end);
 
        /* Now it's safe to release the fragments exclude list. */
        sgen_minor_collector.build_fragments_release_exclude_head ();
@@ -863,21 +894,59 @@ sgen_nursery_alloc_prepare_for_major (void)
 }
 
 void
-sgen_nursery_allocator_set_nursery_bounds (char *start, char *end)
+sgen_nursery_allocator_set_nursery_bounds (char *start, size_t min_size, size_t max_size)
 {
        sgen_nursery_start = start;
-       sgen_nursery_end = end;
+       sgen_nursery_end = start + max_size;
+
+       sgen_nursery_size = min_size;
+       sgen_nursery_min_size = min_size;
+       sgen_nursery_max_size = max_size;
+
+       sgen_nursery_bits = 0;
+       while (ONE_P << (++ sgen_nursery_bits) != sgen_nursery_max_size)
+               ;
 
        /*
         * This will not divide evenly for tiny nurseries (<4kb), so we make sure to be on
         * the right side of things and round up.  We could just do a MIN(1,x) instead,
         * since the nursery size must be a power of 2.
         */
-       sgen_space_bitmap_size = (end - start + SGEN_TO_SPACE_GRANULE_IN_BYTES * 8 - 1) / (SGEN_TO_SPACE_GRANULE_IN_BYTES * 8);
+       sgen_space_bitmap_size = (sgen_nursery_end - sgen_nursery_start + SGEN_TO_SPACE_GRANULE_IN_BYTES * 8 - 1) / (SGEN_TO_SPACE_GRANULE_IN_BYTES * 8);
        sgen_space_bitmap = (char *)g_malloc0 (sgen_space_bitmap_size);
 
        /* Setup the single first large fragment */
-       sgen_minor_collector.init_nursery (&mutator_allocator, start, end);
+       sgen_minor_collector.init_nursery (&mutator_allocator, sgen_nursery_start, sgen_nursery_end);
 }
 
+void
+sgen_resize_nursery (void)
+{
+       size_t major_size;
+
+       if (sgen_nursery_min_size == sgen_nursery_max_size)
+               return;
+
+       major_size = major_collector.get_num_major_sections () * major_collector.section_size + los_memory_usage;
+       /*
+        * We attempt to use a larger nursery size, as long as it doesn't
+        * exceed a certain percentage of the major heap.
+        *
+        * FIXME
+        * Commit memory when expanding and release it when shrinking (which
+        * would only be possible if there aren't any pinned objects in the
+        * section).
+        */
+       if ((sgen_nursery_size * 2) < (major_size / SGEN_DEFAULT_ALLOWANCE_NURSERY_SIZE_RATIO) &&
+                       (sgen_nursery_size * 2) <= sgen_nursery_max_size) {
+               if ((nursery_section->end_data - nursery_section->data) == sgen_nursery_size)
+                       nursery_section->end_data += sgen_nursery_size;
+               sgen_nursery_size *= 2;
+       } else if (sgen_nursery_size > (major_size / SGEN_DEFAULT_ALLOWANCE_NURSERY_SIZE_RATIO) &&
+                       (sgen_nursery_size / 2) >= sgen_nursery_min_size) {
+               sgen_nursery_size /= 2;
+       }
+}
+
+
 #endif