Merge pull request #2800 from BrzVlad/feature-lazy-sweep
authorVlad Brezae <brezaevlad@gmail.com>
Wed, 13 Apr 2016 08:51:25 +0000 (16:51 +0800)
committerVlad Brezae <brezaevlad@gmail.com>
Wed, 13 Apr 2016 08:51:25 +0000 (16:51 +0800)
[sgen] Enable lazy sweep by default

1  2 
mono/metadata/sgen-client-mono.h
mono/sgen/sgen-marksweep.c

index 54630c6ee033853a8ac63065b70eb7cfa1f3a100,230c63deab941b528a2e42ba745cfb7203b496c7..e9f9b1a81a7a5ddeaad89a7ae0ddac596f328fcc
@@@ -3,7 -3,18 +3,7 @@@
   *
   * Copyright (C) 2014 Xamarin Inc
   *
 - * This library is free software; you can redistribute it and/or
 - * modify it under the terms of the GNU Library General Public
 - * License 2.0 as published by the Free Software Foundation;
 - *
 - * This library is distributed in the hope that it will be useful,
 - * but WITHOUT ANY WARRANTY; without even the implied warranty of
 - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 - * Library General Public License for more details.
 - *
 - * You should have received a copy of the GNU Library General Public
 - * License 2.0 along with this library; if not, write to the Free
 - * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 + * Licensed under the MIT license. See LICENSE file in the project root for full license information.
   */
  
  #ifdef SGEN_DEFINE_OBJECT_VTABLE
@@@ -689,6 -700,11 +689,11 @@@ sgen_client_binary_protocol_evacuating_
  {
  }
  
+ static void G_GNUC_UNUSED
+ sgen_client_binary_protocol_concurrent_sweep_end (long long timestamp)
+ {
+ }
  int sgen_thread_handshake (BOOL suspend);
  gboolean sgen_suspend_thread (SgenThreadInfo *info);
  gboolean sgen_resume_thread (SgenThreadInfo *info);
index 38e127b8cd541fe4b304adf4f3e91d19135265db,1509f06988b888be28db3f339ba26a1f9b2e2fd0..d2e3ccdd0093dcc9e68432ef1e8f20b9198651a2
@@@ -7,7 -7,18 +7,7 @@@
   * Copyright 2009-2010 Novell, Inc.
   * Copyright (C) 2012 Xamarin Inc
   *
 - * This library is free software; you can redistribute it and/or
 - * modify it under the terms of the GNU Library General Public
 - * License 2.0 as published by the Free Software Foundation;
 - *
 - * This library is distributed in the hope that it will be useful,
 - * but WITHOUT ANY WARRANTY; without even the implied warranty of
 - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 - * Library General Public License for more details.
 - *
 - * You should have received a copy of the GNU Library General Public
 - * License 2.0 along with this library; if not, write to the Free
 - * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 + * Licensed under the MIT license. See LICENSE file in the project root for full license information.
   */
  
  #include "config.h"
@@@ -99,7 -110,6 +99,7 @@@ struct _MSBlockInfo 
        guint16 obj_size_index;
        /* FIXME: Reduce this - it only needs a byte. */
        volatile gint32 state;
 +      gint16 nused;
        unsigned int pinned : 1;
        unsigned int has_references : 1;
        unsigned int has_pinned : 1;    /* means cannot evacuate */
@@@ -162,7 -172,7 +162,7 @@@ static int fast_block_obj_size_indexes 
  static gboolean *evacuate_block_obj_sizes;
  static float evacuation_threshold = 0.666f;
  
- static gboolean lazy_sweep = FALSE;
+ static gboolean lazy_sweep = TRUE;
  
  enum {
        SWEEP_STATE_SWEPT,
@@@ -807,6 -817,7 +807,7 @@@ set_sweep_state (int new_, int expected
  static gboolean ensure_block_is_checked_for_sweeping (guint32 block_index, gboolean wait, gboolean *have_checked);
  
  static SgenThreadPoolJob * volatile sweep_job;
+ static SgenThreadPoolJob * volatile sweep_blocks_job;
  
  static void
  major_finish_sweep_checking (void)
@@@ -1515,7 -1526,6 +1516,7 @@@ ensure_block_is_checked_for_sweeping (g
        for (i = 0; i < MS_NUM_MARK_WORDS; ++i)
                nused += bitcount (block->mark_words [i]);
  
 +      block->nused = nused;
        if (nused)
                have_live = TRUE;
        if (nused < count)
        return !!tagged_block;
  }
  
+ static void
+ sweep_blocks_job_func (void *thread_data_untyped, SgenThreadPoolJob *job)
+ {
+       volatile gpointer *slot;
+       SGEN_ARRAY_LIST_FOREACH_SLOT (&allocated_blocks, slot) {
+               sweep_block (BLOCK_UNTAG (*slot));
+       } SGEN_ARRAY_LIST_END_FOREACH_SLOT;
+       mono_memory_write_barrier ();
+       sweep_blocks_job = NULL;
+ }
  static void
  sweep_job_func (void *thread_data_untyped, SgenThreadPoolJob *job)
  {
  
        sgen_array_list_remove_nulls (&allocated_blocks);
  
+       /*
+        * Concurrently sweep all the blocks to reduce workload during minor
+        * pauses where we need certain blocks to be swept. At the start of
+        * the next major we need all blocks to be swept anyway.
+        */
+       if (concurrent_sweep && lazy_sweep) {
+               sweep_blocks_job = sgen_thread_pool_job_alloc ("sweep_blocks", sweep_blocks_job_func, sizeof (SgenThreadPoolJob));
+               sgen_thread_pool_job_enqueue (sweep_blocks_job);
+       }
        sweep_finish ();
  
        sweep_job = NULL;
@@@ -1641,9 -1675,9 +1666,11 @@@ sweep_finish (void
                }
        }
  
 +      sgen_memgov_major_post_sweep ();
 +
        set_sweep_state (SWEEP_STATE_SWEPT, SWEEP_STATE_COMPACTING);
+       if (concurrent_sweep)
+               binary_protocol_concurrent_sweep_end (sgen_timestamp ());
  }
  
  static void
@@@ -1780,67 -1814,6 +1807,67 @@@ major_finish_nursery_collection (void
  #endif
  }
  
 +static int
 +block_usage_comparer (const void *bl1, const void *bl2)
 +{
 +      const gint16 nused1 = (*(MSBlockInfo**)bl1)->nused;
 +      const gint16 nused2 = (*(MSBlockInfo**)bl2)->nused;
 +
 +      return nused2 - nused1;
 +}
 +
 +static void
 +sgen_evacuation_freelist_blocks (MSBlockInfo * volatile *block_list, int size_index)
 +{
 +      MSBlockInfo **evacuated_blocks;
 +      size_t index = 0, count, num_blocks = 0, num_used = 0;
 +      MSBlockInfo *info;
 +      MSBlockInfo * volatile *prev;
 +
 +      for (info = *block_list; info != NULL; info = info->next_free) {
 +              num_blocks++;
 +              num_used += info->nused;
 +      }
 +
 +      /*
 +       * We have a set of blocks in the freelist which will be evacuated. Instead
 +       * of evacuating all of the blocks into new ones, we traverse the freelist
 +       * sorting it by the number of occupied slots, evacuating the objects from
 +       * blocks with fewer used slots into fuller blocks.
 +       *
 +       * The number of used slots is set at the end of the previous sweep. Since
 +       * we sequentially unlink slots from blocks, except for the head of the
 +       * freelist, for blocks on the freelist, the number of used slots is the same
 +       * as at the end of the previous sweep.
 +       */
 +      evacuated_blocks = (MSBlockInfo**)sgen_alloc_internal_dynamic (sizeof (MSBlockInfo*) * num_blocks, INTERNAL_MEM_TEMPORARY, TRUE);
 +
 +      for (info = *block_list; info != NULL; info = info->next_free) {
 +              evacuated_blocks [index++] = info;
 +      }
 +
 +      SGEN_ASSERT (0, num_blocks == index, "Why did the freelist change ?");
 +
 +      qsort (evacuated_blocks, num_blocks, sizeof (gpointer), block_usage_comparer);
 +
 +      /*
 +       * Form a new freelist with the fullest blocks. These blocks will also be
 +       * marked as to_space so we don't evacuate from them.
 +       */
 +      count = MS_BLOCK_FREE / block_obj_sizes [size_index];
 +      prev = block_list;
 +      for (index = 0; index < (num_used + count - 1) / count; index++) {
 +              SGEN_ASSERT (0, index < num_blocks, "Why do we need more blocks for compaction than we already had ?");
 +              info = evacuated_blocks [index];
 +              info->is_to_space = TRUE;
 +              *prev = info;
 +              prev = &info->next_free;
 +      }
 +      *prev = NULL;
 +
 +      sgen_free_internal_dynamic (evacuated_blocks, sizeof (MSBlockInfo*) * num_blocks, INTERNAL_MEM_TEMPORARY);
 +}
 +
  static void
  major_start_major_collection (void)
  {
  
                binary_protocol_evacuating_blocks (block_obj_sizes [i]);
  
 -              free_block_lists [0][i] = NULL;
 -              free_block_lists [MS_BLOCK_FLAG_REFS][i] = NULL;
 +              sgen_evacuation_freelist_blocks (&free_block_lists [0][i], i);
 +              sgen_evacuation_freelist_blocks (&free_block_lists [MS_BLOCK_FLAG_REFS][i], i);
        }
  
-       if (lazy_sweep)
-               binary_protocol_sweep_begin (GENERATION_OLD, TRUE);
+       if (lazy_sweep && concurrent_sweep) {
+               /*
+                * sweep_blocks_job is created before sweep_finish, which we wait for above
+                * (major_finish_sweep_checking). After the end of sweep, if we don't have
+                * sweep_blocks_job set, it means that it has already been run.
+                */
+               SgenThreadPoolJob *job = sweep_blocks_job;
+               if (job)
+                       sgen_thread_pool_job_wait (job);
+       }
  
+       if (lazy_sweep && !concurrent_sweep)
+               binary_protocol_sweep_begin (GENERATION_OLD, TRUE);
        /* Sweep all unswept blocks and set them to MARKING */
        FOREACH_BLOCK_NO_LOCK (block) {
-               if (lazy_sweep)
+               if (lazy_sweep && !concurrent_sweep)
                        sweep_block (block);
                SGEN_ASSERT (0, block->state == BLOCK_STATE_SWEPT, "All blocks must be swept when we're pinning.");
                set_block_state (block, BLOCK_STATE_MARKING, BLOCK_STATE_SWEPT);
 +              /*
 +               * Swept blocks that have a null free_list are full. Evacuation is not
 +               * effective on these blocks since we expect them to have high usage anyway,
 +               * given that the survival rate for majors is relatively high.
 +               */
 +              if (evacuate_block_obj_sizes [block->obj_size_index] && !block->free_list)
 +                      block->is_to_space = TRUE;
        } END_FOREACH_BLOCK_NO_LOCK;
-       if (lazy_sweep)
+       if (lazy_sweep && !concurrent_sweep)
                binary_protocol_sweep_end (GENERATION_OLD, TRUE);
  
        set_sweep_state (SWEEP_STATE_NEED_SWEEPING, SWEEP_STATE_SWEPT);