summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKevin Buhr <buhr@asaurus.net>2019-07-13 12:56:35 -0500
committerMarge Bot <ben+marge-bot@smart-cactus.org>2019-12-31 23:44:39 -0500
commit859ebdd446eda446d38708a587503c255b58c4c6 (patch)
tree46892f2b488b5f272ba67680fd68ba6b7ecf5ef8
parentd710fd666a70ae100120042c8549634537001609 (diff)
downloadhaskell-859ebdd446eda446d38708a587503c255b58c4c6.tar.gz
Add "-Iw" RTS flag for minimum wait between idle GCs (#11134)
-rw-r--r--docs/users_guide/runtime_control.rst20
-rw-r--r--includes/rts/Flags.h1
-rw-r--r--rts/RtsFlags.c41
-rw-r--r--rts/Timer.c21
4 files changed, 63 insertions, 20 deletions
diff --git a/docs/users_guide/runtime_control.rst b/docs/users_guide/runtime_control.rst
index 738d981a18..0b9cdfbfbc 100644
--- a/docs/users_guide/runtime_control.rst
+++ b/docs/users_guide/runtime_control.rst
@@ -653,6 +653,26 @@ performance.
This is an experimental feature, please let us know if it causes
problems and/or could benefit from further tuning.
+.. rts-flag:: -Iw ⟨seconds⟩
+
+ :default: 0 seconds
+
+ .. index::
+ single: idle GC
+
+ By default, if idle GC is enabled in the threaded runtime, a major
+ GC will be performed every time the process goes idle for a
+ sufficiently long duration (see :rts-flag:`-I ⟨seconds⟩`). For
+ large server processes accepting regular but infrequent requests
+ (e.g., once per second), an expensive, major GC may run after
+ every request. As an alternative to shutting off idle GC entirely
+ (with ``-I0``), a minimum wait time between idle GCs can be
+ specified with this flag. For example, ``-Iw60`` will ensure that
+ an idle GC runs at most once per minute.
+
+ This is an experimental feature, please let us know if it causes
+ problems and/or could benefit from further tuning.
+
.. rts-flag:: -ki ⟨size⟩
:default: 1k
diff --git a/includes/rts/Flags.h b/includes/rts/Flags.h
index 4af19aa953..d0c41a1576 100644
--- a/includes/rts/Flags.h
+++ b/includes/rts/Flags.h
@@ -66,6 +66,7 @@ typedef struct _GC_FLAGS {
bool ringBell;
Time idleGCDelayTime; /* units: TIME_RESOLUTION */
+ Time interIdleGCWait; /* units: TIME_RESOLUTION */
bool doIdleGC;
Time longGCSync; /* units: TIME_RESOLUTION */
diff --git a/rts/RtsFlags.c b/rts/RtsFlags.c
index 2c5f69a76f..4d40810bc6 100644
--- a/rts/RtsFlags.c
+++ b/rts/RtsFlags.c
@@ -164,6 +164,7 @@ void initRtsFlagsDefaults(void)
RtsFlags.GcFlags.compactThreshold = 30.0;
RtsFlags.GcFlags.sweep = false;
RtsFlags.GcFlags.idleGCDelayTime = USToTime(300000); // 300ms
+ RtsFlags.GcFlags.interIdleGCWait = 0;
#if defined(THREADED_RTS)
RtsFlags.GcFlags.doIdleGC = true;
#else
@@ -1179,19 +1180,33 @@ error = true;
break;
case 'I': /* idle GC delay */
- OPTION_UNSAFE;
- if (rts_argv[arg][2] == '\0') {
- /* use default */
- } else {
- Time t = fsecondsToTime(atof(rts_argv[arg]+2));
- if (t == 0) {
- RtsFlags.GcFlags.doIdleGC = false;
- } else {
- RtsFlags.GcFlags.doIdleGC = true;
- RtsFlags.GcFlags.idleGCDelayTime = t;
- }
- }
- break;
+ OPTION_UNSAFE;
+ switch (rts_argv[arg][2]) {
+ /* minimum inter-idle GC wait time */
+ case 'w':
+ if (rts_argv[arg][3] == '\0') {
+ /* use default */
+ } else {
+ RtsFlags.GcFlags.interIdleGCWait = fsecondsToTime(atof(rts_argv[arg]+3));
+ }
+ break;
+ /* idle delay before GC */
+ case '\0':
+ /* use default */
+ break;
+ default:
+ {
+ Time t = fsecondsToTime(atof(rts_argv[arg]+2));
+ if (t == 0) {
+ RtsFlags.GcFlags.doIdleGC = false;
+ } else {
+ RtsFlags.GcFlags.doIdleGC = true;
+ RtsFlags.GcFlags.idleGCDelayTime = t;
+ }
+ }
+ break;
+ }
+ break;
case 'T':
OPTION_SAFE;
diff --git a/rts/Timer.c b/rts/Timer.c
index 8e2fb4a835..4fafbe09e8 100644
--- a/rts/Timer.c
+++ b/rts/Timer.c
@@ -28,8 +28,11 @@
/* ticks left before next pre-emptive context switch */
static int ticks_to_ctxt_switch = 0;
-/* idle ticks left before we perform a GC */
-static int ticks_to_gc = 0;
+/* idle ticks left before GC allowed */
+static int idle_ticks_to_gc = 0;
+
+/* inter-idle GC ticks left before GC allowed */
+static int inter_gc_ticks_to_gc = 0;
/*
* Function: handle_tick()
@@ -53,18 +56,21 @@ handle_tick(int unused STG_UNUSED)
/*
* If we've been inactive for idleGCDelayTime (set by +RTS
* -I), tell the scheduler to wake up and do a GC, to check
- * for threads that are deadlocked.
+ * for threads that are deadlocked. However, ensure we wait
+ * at least interIdleGCWait (+RTS -Iw) between idle GCs.
*/
switch (recent_activity) {
case ACTIVITY_YES:
recent_activity = ACTIVITY_MAYBE_NO;
- ticks_to_gc = RtsFlags.GcFlags.idleGCDelayTime /
- RtsFlags.MiscFlags.tickInterval;
+ idle_ticks_to_gc = RtsFlags.GcFlags.idleGCDelayTime /
+ RtsFlags.MiscFlags.tickInterval;
break;
case ACTIVITY_MAYBE_NO:
- if (ticks_to_gc == 0) {
+ if (idle_ticks_to_gc == 0 && inter_gc_ticks_to_gc == 0) {
if (RtsFlags.GcFlags.doIdleGC) {
recent_activity = ACTIVITY_INACTIVE;
+ inter_gc_ticks_to_gc = RtsFlags.GcFlags.interIdleGCWait /
+ RtsFlags.MiscFlags.tickInterval;
#if defined(THREADED_RTS)
wakeUpRts();
// The scheduler will call stopTimer() when it has done
@@ -86,7 +92,8 @@ handle_tick(int unused STG_UNUSED)
#endif
}
} else {
- ticks_to_gc--;
+ if (idle_ticks_to_gc) idle_ticks_to_gc--;
+ if (inter_gc_ticks_to_gc) inter_gc_ticks_to_gc--;
}
break;
default: