summaryrefslogtreecommitdiff
path: root/rts/Capability.c
diff options
context:
space:
mode:
authorSimon Marlow <marlowsd@gmail.com>2016-04-09 20:45:50 +0100
committerSimon Marlow <smarlow@fb.com>2016-05-04 05:30:30 -0700
commit76ee260778991367b8dbf07ecf7afd31f826c824 (patch)
treefbddddf878413dab3c01abf8108c26d2bd20db4c /rts/Capability.c
parentf9d93751126e58fb990335095e02fd81a3595fde (diff)
downloadhaskell-76ee260778991367b8dbf07ecf7afd31f826c824.tar.gz
Allow limiting the number of GC threads (+RTS -qn<n>)
This allows the GC to use fewer threads than the number of capabilities. At each GC, we choose some of the capabilities to be "idle", which means that the thread running on that capability (if any) will sleep for the duration of the GC, and the other threads will do its work. We choose capabilities that are already idle (if any) to be the idle capabilities. The idea is that this helps in the following situation: * We want to use a large -N value so as to make use of hyperthreaded cores * We use a large heap size, so GC is infrequent * But we don't want to use all -N threads in the GC, because that thrashes the memory too much. See docs for usage.
Diffstat (limited to 'rts/Capability.c')
-rw-r--r--rts/Capability.c48
1 files changed, 30 insertions, 18 deletions
diff --git a/rts/Capability.c b/rts/Capability.c
index 355f36d0c5..aa77d1b357 100644
--- a/rts/Capability.c
+++ b/rts/Capability.c
@@ -55,10 +55,9 @@ static Capability *last_free_capability = NULL;
/*
* Indicates that the RTS wants to synchronise all the Capabilities
- * for some reason. All Capabilities should stop and return to the
- * scheduler.
+ * for some reason. All Capabilities should yieldCapability().
*/
-volatile StgWord pending_sync = 0;
+PendingSync * volatile pending_sync = 0;
/* Let foreign code get the current Capability -- assuming there is one!
* This is useful for unsafe foreign calls because they are called with
@@ -477,13 +476,19 @@ releaseCapability_ (Capability* cap,
return;
}
- // If there is a pending sync, then we should just leave the
- // Capability free. The thread trying to sync will be about to
- // call waitForCapability().
- if (pending_sync != 0 && pending_sync != SYNC_GC_PAR) {
- last_free_capability = cap; // needed?
- debugTrace(DEBUG_sched, "sync pending, set capability %d free", cap->no);
- return;
+ // If there is a pending sync, then we should just leave the Capability
+ // free. The thread trying to sync will be about to call
+ // waitForCapability().
+ //
+ // Note: this is *after* we check for a returning task above,
+ // because the task attempting to acquire all the capabilities may
+ // be currently in waitForCapability() waiting for this
+ // capability, in which case simply setting it as free would not
+ // wake up the waiting task.
+ PendingSync *sync = pending_sync;
+ if (sync && (sync->type != SYNC_GC_PAR || sync->idle[cap->no])) {
+ debugTrace(DEBUG_sched, "sync pending, freeing capability %d", cap->no);
+ return;
}
// If the next thread on the run queue is a bound thread,
@@ -795,14 +800,21 @@ yieldCapability (Capability** pCap, Task *task, rtsBool gcAllowed)
{
Capability *cap = *pCap;
- if ((pending_sync == SYNC_GC_PAR) && gcAllowed) {
- traceEventGcStart(cap);
- gcWorkerThread(cap);
- traceEventGcEnd(cap);
- traceSparkCounters(cap);
- // See Note [migrated bound threads 2]
- if (task->cap == cap) {
- return rtsTrue;
+ if (gcAllowed)
+ {
+ PendingSync *sync = pending_sync;
+
+ if (sync && sync->type == SYNC_GC_PAR) {
+ if (! sync->idle[cap->no]) {
+ traceEventGcStart(cap);
+ gcWorkerThread(cap);
+ traceEventGcEnd(cap);
+ traceSparkCounters(cap);
+ // See Note [migrated bound threads 2]
+ if (task->cap == cap) {
+ return rtsTrue;
+ }
+ }
}
}