.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
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
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
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.
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;
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);
#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.
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
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);