Merge pull request #5002 from BrzVlad/feature-sgen-modes
authorVlad Brezae <brezaevlad@gmail.com>
Tue, 13 Jun 2017 11:14:47 +0000 (14:14 +0300)
committerGitHub <noreply@github.com>
Tue, 13 Jun 2017 11:14:47 +0000 (14:14 +0300)
[sgen] SGen modes

1  2 
man/mono.1
mono/sgen/sgen-conf.h
mono/sgen/sgen-gc.c

diff --combined man/mono.1
index e822ff94c89b2aa98b745633ab4fcff87fbdb2ba,17f8f99d2aa57e4f184faa7761d33d5c5549b410..89a051e250618a0df87ccdda255d69d6c35ebbc6
@@@ -181,16 -181,6 +181,16 @@@ Gives the path for the temporary LLVM b
  .I info
  Print the architecture the AOT in this copy of Mono targets and quit.
  .TP
 +.I interp
 +Generates all required wrappers, so that it is possible to run --interpreter without
 +any code generation at runtime.  This option only makes sense with \fBmscorlib.dll\fR.
 +Embedders can set
 +
 +.nf
 +mono_jit_set_aot_mode (MONO_AOT_MODE_INTERP);
 +.fi
 +.ne
 +.TP
  .I ld-flags
  Additional flags to pass to the C linker (if the current AOT mode calls for invoking it).
  .TP
@@@ -1101,6 -1091,11 +1101,6 @@@ code generation backend.   For example 
  compile 10 methods with LLVM and then switch to the Mono JIT engine.
  \fBLLVM_COUNT=0\fR would disable the LLVM engine altogether.
  .TP
 -\fBMONO_AOT_CACHE\fR
 -If set, this variable will instruct Mono to ahead-of-time compile new
 -assemblies on demand and store the result into a cache in
 -~/.mono/aot-cache. 
 -.TP
  \fBMONO_ASPNET_INHIBIT_SETTINGSMAP\fR
  Mono contains a feature which allows modifying settings in the .config files shipped
  with Mono by using config section mappers. The mappers and the mapping rules are
@@@ -1309,10 -1304,19 +1309,19 @@@ first generation (of two).  A larger nu
  program but will obviously use more memory.  The default nursery size
  4 MB.
  .TP
- \fBmajor=\fIcollector\fR Specifies which major collector to use.
- Options are `marksweep' for the Mark&Sweep collector, and
- `marksweep-conc' for concurrent Mark&Sweep.  The non-concurrent
- Mark&Sweep collector is the default.
+ \fBmajor=\fIcollector\fR
+ Specifies which major collector to use.
+ Options are `marksweep' for the Mark&Sweep collector, `marksweep-conc'
+ for concurrent Mark&Sweep and `marksweep-conc-par' for parallel and
+ concurrent Mark&Sweep.  The concurrent Mark&Sweep collector is the default.
+ .TP
+ \fBmode=balanced|throughput|pause\fR[:\fImax-pause\fR]
+ Specifies what should be the garbage collector's target. The `throughput'
+ mode aims to reduce time spent in the garbage collector and improve
+ application speed, the `pause' mode aims to keep pause times to a minimum
+ and it receives the argument \fImax-pause\fR which specifies the maximum
+ pause time in milliseconds that is acceptable and the `balanced' mode
+ which is a general purpose optimal mode.
  .TP
  \fBsoft-heap-limit=\fIsize\fR
  Once the heap size gets larger than this size, ignore what the default
@@@ -1375,9 -1379,11 +1384,11 @@@ more memory when it reaches a stable si
  This option is EXPERIMENTAL, so it might disappear in later versions of mono.
  .TP
  \fBminor=\fIminor-collector\fR
- Specifies which minor collector to use. Options are 'simple' which
- promotes all objects from the nursery directly to the old generation
- and 'split' which lets object stay longer on the nursery before promoting.
+ Specifies which minor collector to use. Options are `simple' which
+ promotes all objects from the nursery directly to the old generation,
+ `simple-par' which has same promotion behavior as `simple' but using
+ multiple workers and `split' which lets objects stay longer on the nursery
+ before promoting.
  .TP
  \fBalloc-ratio=\fIratio\fR
  Specifies the ratio of memory from the nursery to be use by the alloc space.
diff --combined mono/sgen/sgen-conf.h
index f6738e4d6eb9d34232ecfb23c729eb97266808cf,ce724e656a2e22c614f707145fc68c6d1a13fac3..53e962c2b702e7ea8a893dc940ea8d42d227b967
@@@ -216,13 -216,10 +216,16 @@@ typedef mword SgenDescriptor
   * sizing heuristics. We are keeping leeway in order to be prepared for work-load
   * variations.
   */
- #define SGEN_MAX_PAUSE_TIME 30
- #define SGEN_MAX_PAUSE_MARGIN 0.66f
+ #define SGEN_DEFAULT_MAX_PAUSE_TIME 30
+ #define SGEN_DEFAULT_MAX_PAUSE_MARGIN 0.66f
+ #define SGEN_PAUSE_MODE_MAX_PAUSE_MARGIN 0.5f
  
 +/*
 + * In practice, for nurseries smaller than this, the parallel minor tends to be
 + * ineffective, even leading to regressions. Avoid using it for smaller nurseries.
 + */
 +#define SGEN_PARALLEL_MINOR_MIN_NURSERY_SIZE (1 << 24)
 +
  #endif
diff --combined mono/sgen/sgen-gc.c
index 5213dda70ee2f457e478af3d58cc3862abec292c,b87bb300fe5c4a5fafa7cdc16cc87ba9e0b3635f..6cd939553a99cb91d0553e6a2e80e035fccbae43
@@@ -236,6 -236,9 +236,9 @@@ static gboolean do_dump_nursery_conten
  static gboolean enable_nursery_canaries = FALSE;
  
  static gboolean precleaning_enabled = TRUE;
+ static gboolean dynamic_nursery = FALSE;
+ static size_t min_nursery_size = 0;
+ static size_t max_nursery_size = 0;
  
  #ifdef HEAVY_STATISTICS
  guint64 stat_objects_alloced_degraded = 0;
@@@ -288,8 -291,8 +291,8 @@@ static guint64 time_major_fragment_crea
  
  static guint64 time_max = 0;
  
- static int sgen_max_pause_time = SGEN_MAX_PAUSE_TIME;
- static float sgen_max_pause_margin = SGEN_MAX_PAUSE_MARGIN;
+ static int sgen_max_pause_time = SGEN_DEFAULT_MAX_PAUSE_TIME;
+ static float sgen_max_pause_margin = SGEN_DEFAULT_MAX_PAUSE_MARGIN;
  
  static SGEN_TV_DECLARE (time_major_conc_collection_start);
  static SGEN_TV_DECLARE (time_major_conc_collection_end);
@@@ -333,13 -336,32 +336,32 @@@ nursery_canaries_enabled (void
  
  #if defined(HAVE_CONC_GC_AS_DEFAULT)
  /* Use concurrent major on deskstop platforms */
- #define DEFAULT_MAJOR_INIT sgen_marksweep_conc_init
- #define DEFAULT_MAJOR_NAME "marksweep-conc"
+ #define DEFAULT_MAJOR SGEN_MAJOR_CONCURRENT
  #else
- #define DEFAULT_MAJOR_INIT sgen_marksweep_init
- #define DEFAULT_MAJOR_NAME "marksweep"
+ #define DEFAULT_MAJOR SGEN_MAJOR_SERIAL
  #endif
  
+ typedef enum {
+       SGEN_MAJOR_DEFAULT,
+       SGEN_MAJOR_SERIAL,
+       SGEN_MAJOR_CONCURRENT,
+       SGEN_MAJOR_CONCURRENT_PARALLEL
+ } SgenMajor;
+ typedef enum {
+       SGEN_MINOR_DEFAULT,
+       SGEN_MINOR_SIMPLE,
+       SGEN_MINOR_SIMPLE_PARALLEL,
+       SGEN_MINOR_SPLIT
+ } SgenMinor;
+ typedef enum {
+       SGEN_MODE_NONE,
+       SGEN_MODE_BALANCED,
+       SGEN_MODE_THROUGHPUT,
+       SGEN_MODE_PAUSE
+ } SgenMode;
  /*
   * ######################################################################
   * ########  Global data.
@@@ -1622,7 -1644,6 +1644,6 @@@ collect_nursery (const char *reason, gb
        SgenGrayQueue gc_thread_gray_queue;
        SgenObjectOperations *object_ops_nopar, *object_ops_par = NULL;
        ScanCopyContext ctx;
-       int duration;
        TV_DECLARE (atv);
        TV_DECLARE (btv);
        SGEN_TV_DECLARE (last_minor_collection_start_tv);
                object_ops_nopar = &sgen_minor_collector.serial_ops_with_concurrent_major;
        } else {
                object_ops_nopar = &sgen_minor_collector.serial_ops;
 -              if (sgen_minor_collector.is_parallel) {
 +              if (sgen_minor_collector.is_parallel && sgen_nursery_size >= SGEN_PARALLEL_MINOR_MIN_NURSERY_SIZE) {
                        object_ops_par = &sgen_minor_collector.parallel_ops;
                        is_parallel = TRUE;
                }
                sgen_check_remset_consistency ();
  
  
-       TV_GETTIME (btv);
-       duration = (int)(TV_ELAPSED (last_minor_collection_start_tv, btv) / 10000);
-       if (duration > (sgen_max_pause_time * sgen_max_pause_margin))
-               sgen_resize_nursery (TRUE);
-       else
-               sgen_resize_nursery (FALSE);
+       if (sgen_max_pause_time) {
+               int duration;
+               TV_GETTIME (btv);
+               duration = (int)(TV_ELAPSED (last_minor_collection_start_tv, btv) / 10000);
+               if (duration > (sgen_max_pause_time * sgen_max_pause_margin))
+                       sgen_resize_nursery (TRUE);
+               else
+                       sgen_resize_nursery (FALSE);
+       } else {
+                       sgen_resize_nursery (FALSE);
+       }
  
        /* walk the pin_queue, build up the fragment list of free memory, unmark
         * pinned objects as we go, memzero() the empty fragments so they are ready for the
@@@ -2999,13 -3026,162 +3026,162 @@@ parse_double_in_interval (const char *e
        return TRUE;
  }
  
+ static SgenMinor
+ parse_sgen_minor (const char *opt)
+ {
+       if (!opt)
+               return SGEN_MINOR_DEFAULT;
+       if (!strcmp (opt, "simple")) {
+               return SGEN_MINOR_SIMPLE;
+       } else if (!strcmp (opt, "simple-par")) {
+               return SGEN_MINOR_SIMPLE_PARALLEL;
+       } else if (!strcmp (opt, "split")) {
+               return SGEN_MINOR_SPLIT;
+       } else {
+               sgen_env_var_error (MONO_GC_PARAMS_NAME, "Using default instead.", "Unknown minor collector `%s'.", opt);
+               return SGEN_MINOR_DEFAULT;
+       }
+ }
+ static SgenMajor
+ parse_sgen_major (const char *opt)
+ {
+       if (!opt)
+               return SGEN_MAJOR_DEFAULT;
+       if (!strcmp (opt, "marksweep")) {
+               return SGEN_MAJOR_SERIAL;
+       } else if (!strcmp (opt, "marksweep-conc")) {
+               return SGEN_MAJOR_CONCURRENT;
+       } else if (!strcmp (opt, "marksweep-conc-par")) {
+               return SGEN_MAJOR_CONCURRENT_PARALLEL;
+       } else {
+               sgen_env_var_error (MONO_GC_PARAMS_NAME, "Using default instead.", "Unknown major collector `%s'.", opt);
+               return SGEN_MAJOR_DEFAULT;
+       }
+ }
+ static SgenMode
+ parse_sgen_mode (const char *opt)
+ {
+       if (!opt)
+               return SGEN_MODE_NONE;
+       if (!strcmp (opt, "balanced")) {
+               return SGEN_MODE_BALANCED;
+       } else if (!strcmp (opt, "throughput")) {
+               return SGEN_MODE_THROUGHPUT;
+       } else if (!strcmp (opt, "pause") || g_str_has_prefix (opt, "pause:")) {
+               return SGEN_MODE_PAUSE;
+       } else {
+               sgen_env_var_error (MONO_GC_PARAMS_NAME, "Using default configurations.", "Unknown mode `%s'.", opt);
+               return SGEN_MODE_NONE;
+       }
+ }
+ static void
+ init_sgen_minor (SgenMinor minor)
+ {
+       switch (minor) {
+       case SGEN_MINOR_DEFAULT:
+       case SGEN_MINOR_SIMPLE:
+               sgen_simple_nursery_init (&sgen_minor_collector, FALSE);
+               break;
+       case SGEN_MINOR_SIMPLE_PARALLEL:
+               sgen_simple_nursery_init (&sgen_minor_collector, TRUE);
+               break;
+       case SGEN_MINOR_SPLIT:
+               sgen_split_nursery_init (&sgen_minor_collector);
+               break;
+       default:
+               g_assert_not_reached ();
+       }
+ }
+ static void
+ init_sgen_major (SgenMajor major)
+ {
+       if (major == SGEN_MAJOR_DEFAULT)
+               major = DEFAULT_MAJOR;
+       switch (major) {
+       case SGEN_MAJOR_SERIAL:
+               sgen_marksweep_init (&major_collector);
+               break;
+       case SGEN_MAJOR_CONCURRENT:
+               sgen_marksweep_conc_init (&major_collector);
+               break;
+       case SGEN_MAJOR_CONCURRENT_PARALLEL:
+               sgen_marksweep_conc_par_init (&major_collector);
+               break;
+       default:
+               g_assert_not_reached ();
+       }
+ }
+ /*
+  * If sgen mode is set, major/minor configuration is fixed. The other gc_params
+  * are parsed and processed after major/minor initialization, so it can potentially
+  * override some knobs set by the sgen mode. We can consider locking out additional
+  * configurations when gc_modes are used.
+  */
+ static void
+ init_sgen_mode (SgenMode mode)
+ {
+       SgenMinor minor = SGEN_MINOR_DEFAULT;
+       SgenMajor major = SGEN_MAJOR_DEFAULT;
+       switch (mode) {
+       case SGEN_MODE_BALANCED:
+               /*
+                * Use a dynamic parallel nursery with a major concurrent collector.
+                * This uses the default values for max pause time and nursery size.
+                */
+               minor = SGEN_MINOR_SIMPLE;
+               major = SGEN_MAJOR_CONCURRENT;
+               dynamic_nursery = TRUE;
+               break;
+       case SGEN_MODE_THROUGHPUT:
+               /*
+                * Use concurrent major to let the mutator do more work. Use a larger
+                * nursery, without pause time constraints, in order to collect more
+                * objects in parallel and avoid repetitive collection tasks (pinning,
+                * root scanning etc)
+                */
+               minor = SGEN_MINOR_SIMPLE_PARALLEL;
+               major = SGEN_MAJOR_CONCURRENT;
+               dynamic_nursery = TRUE;
+               sgen_max_pause_time = 0;
+               break;
+       case SGEN_MODE_PAUSE:
+               /*
+                * Use concurrent major and dynamic nursery with a more
+                * aggressive shrinking relative to pause times.
+                * FIXME use parallel minors
+                */
+               minor = SGEN_MINOR_SIMPLE;
+               major = SGEN_MAJOR_CONCURRENT;
+               dynamic_nursery = TRUE;
+               sgen_max_pause_margin = SGEN_PAUSE_MODE_MAX_PAUSE_MARGIN;
+               break;
+       default:
+               g_assert_not_reached ();
+       }
+       init_sgen_minor (minor);
+       init_sgen_major (major);
+ }
  void
  sgen_gc_init (void)
  {
        char *env;
        char **opts, **ptr;
-       char *major_collector_opt = NULL;
-       char *minor_collector_opt = NULL;
+       SgenMajor sgen_major = SGEN_MAJOR_DEFAULT;
+       SgenMinor sgen_minor = SGEN_MINOR_DEFAULT;
+       SgenMode sgen_mode = SGEN_MODE_NONE;
        char *params_opts = NULL;
        char *debug_opts = NULL;
        size_t max_heap = 0;
        gboolean debug_print_allowance = FALSE;
        double allowance_ratio = 0, save_target = 0;
        gboolean cement_enabled = TRUE;
-       gboolean dynamic_nursery = FALSE;
-       size_t min_nursery_size = 0, max_nursery_size = 0;
  
        do {
                result = InterlockedCompareExchange (&gc_initialized, -1, 0);
                        char *opt = *ptr;
                        if (g_str_has_prefix (opt, "major=")) {
                                opt = strchr (opt, '=') + 1;
-                               major_collector_opt = g_strdup (opt);
+                               sgen_major = parse_sgen_major (opt);
                        } else if (g_str_has_prefix (opt, "minor=")) {
                                opt = strchr (opt, '=') + 1;
-                               minor_collector_opt = g_strdup (opt);
+                               sgen_minor = parse_sgen_minor (opt);
+                       } else if (g_str_has_prefix (opt, "mode=")) {
+                               opt = strchr (opt, '=') + 1;
+                               sgen_mode = parse_sgen_mode (opt);
                        }
                }
        } else {
  
        sgen_client_init ();
  
-       if (!minor_collector_opt) {
-               sgen_simple_nursery_init (&sgen_minor_collector, FALSE);
-       } else {
-               if (!strcmp (minor_collector_opt, "simple")) {
-               use_simple_nursery:
-                       sgen_simple_nursery_init (&sgen_minor_collector, FALSE);
-               } else if (!strcmp (minor_collector_opt, "simple-par")) {
-                       sgen_simple_nursery_init (&sgen_minor_collector, TRUE);
-               } else if (!strcmp (minor_collector_opt, "split")) {
-                       sgen_split_nursery_init (&sgen_minor_collector);
-               } else {
-                       sgen_env_var_error (MONO_GC_PARAMS_NAME, "Using `simple` instead.", "Unknown minor collector `%s'.", minor_collector_opt);
-                       goto use_simple_nursery;
-               }
-       }
-       if (!major_collector_opt) {
-       use_default_major:
-               DEFAULT_MAJOR_INIT (&major_collector);
-       } else if (!strcmp (major_collector_opt, "marksweep")) {
-               sgen_marksweep_init (&major_collector);
-       } else if (!strcmp (major_collector_opt, "marksweep-conc")) {
-               sgen_marksweep_conc_init (&major_collector);
-       } else if (!strcmp (major_collector_opt, "marksweep-conc-par")) {
-               sgen_marksweep_conc_par_init (&major_collector);
+       if (sgen_mode != SGEN_MODE_NONE) {
+               if (sgen_minor != SGEN_MINOR_DEFAULT || sgen_major != SGEN_MAJOR_DEFAULT)
+                       sgen_env_var_error (MONO_GC_PARAMS_NAME, "Ignoring major/minor configuration", "Major/minor configurations cannot be used with sgen modes");
+               init_sgen_mode (sgen_mode);
        } else {
-               sgen_env_var_error (MONO_GC_PARAMS_NAME, "Using `" DEFAULT_MAJOR_NAME "` instead.", "Unknown major collector `%s'.", major_collector_opt);
-               goto use_default_major;
+               init_sgen_minor (sgen_minor);
+               init_sgen_major (sgen_major);
        }
  
        if (opts) {
                                continue;
                        if (g_str_has_prefix (opt, "minor="))
                                continue;
+                       if (g_str_has_prefix (opt, "mode=")) {
+                               if (g_str_has_prefix (opt, "mode=pause:")) {
+                                       char *str_pause = strchr (opt, ':') + 1;
+                                       int pause = atoi (str_pause);
+                                       if (pause)
+                                               sgen_max_pause_time = pause;
+                                       else
+                                               sgen_env_var_error (MONO_GC_PARAMS_NAME, "Using default", "Invalid maximum pause time for `pause` sgen mode");
+                               }
+                               continue;
+                       }
                        if (g_str_has_prefix (opt, "max-heap-size=")) {
                                size_t page_size = mono_pagesize ();
                                size_t max_heap_candidate = 0;
                        fprintf (stderr, "\n%s must be a comma-delimited list of one or more of the following:\n", MONO_GC_PARAMS_NAME);
                        fprintf (stderr, "  max-heap-size=N (where N is an integer, possibly with a k, m or a g suffix)\n");
                        fprintf (stderr, "  soft-heap-limit=n (where N is an integer, possibly with a k, m or a g suffix)\n");
+                       fprintf (stderr, "  mode=MODE (where MODE is 'balanced', 'throughput' or 'pause[:N]' and N is maximum pause in milliseconds)\n");
                        fprintf (stderr, "  nursery-size=N (where N is an integer, possibly with a k, m or a g suffix)\n");
                        fprintf (stderr, "  major=COLLECTOR (where COLLECTOR is `marksweep', `marksweep-conc', `marksweep-par')\n");
                        fprintf (stderr, "  minor=COLLECTOR (where COLLECTOR is `simple' or `split')\n");
                g_strfreev (opts);
        }
  
-       if (major_collector_opt)
-               g_free (major_collector_opt);
-       if (minor_collector_opt)
-               g_free (minor_collector_opt);
        if (params_opts)
                g_free (params_opts);
  
        if (major_collector.is_concurrent || sgen_minor_collector.is_parallel) {
                int num_workers = 1;
                if (major_collector.is_parallel || sgen_minor_collector.is_parallel) {
 -                      /* FIXME Detect the number of physical cores, instead of logical */
 -                      num_workers = mono_cpu_count () / 2;
 -                      if (num_workers < 1)
 +                      num_workers = mono_cpu_count ();
 +                      if (num_workers <= 1) {
                                num_workers = 1;
 +                              major_collector.is_parallel = FALSE;
 +                              sgen_minor_collector.is_parallel = FALSE;
 +                      }
                }
 -              sgen_workers_init (num_workers, (SgenWorkerCallback) major_collector.worker_init_cb);
 +              if (major_collector.is_concurrent || sgen_minor_collector.is_parallel)
 +                      sgen_workers_init (num_workers, (SgenWorkerCallback) major_collector.worker_init_cb);
        }
  
        sgen_memgov_init (max_heap, soft_limit, debug_print_allowance, allowance_ratio, save_target);