summaryrefslogtreecommitdiff
path: root/rts/Sparks.c
diff options
context:
space:
mode:
authorSimon Marlow <marlowsd@gmail.com>2008-10-22 09:27:44 +0000
committerSimon Marlow <marlowsd@gmail.com>2008-10-22 09:27:44 +0000
commit99df892cc9620fcc92747b79bba75dad8a1d295c (patch)
tree536df57e1d9975f88ce781627bb2dacaee5b2c0c /rts/Sparks.c
parentcf9650f2a1690c04051c716124bb0350adc74ae7 (diff)
downloadhaskell-99df892cc9620fcc92747b79bba75dad8a1d295c.tar.gz
Refactoring and reorganisation of the scheduler
Change the way we look for work in the scheduler. Previously, checking to see whether there was anything to do was a non-side-effecting operation, but this has changed now that we do work-stealing. This lead to a refactoring of the inner loop of the scheduler. Also, lots of cleanup in the new work-stealing code, but no functional changes. One new statistic is added to the +RTS -s output: SPARKS: 1430 (2 converted, 1427 pruned) lets you know something about the use of `par` in the program.
Diffstat (limited to 'rts/Sparks.c')
-rw-r--r--rts/Sparks.c125
1 files changed, 58 insertions, 67 deletions
diff --git a/rts/Sparks.c b/rts/Sparks.c
index ac11172a9d..360ea41a05 100644
--- a/rts/Sparks.c
+++ b/rts/Sparks.c
@@ -53,9 +53,9 @@
/* internal helpers ... */
-StgWord roundUp2(StgWord val);
-
-StgWord roundUp2(StgWord val) {
+static StgWord
+roundUp2(StgWord val)
+{
StgWord rounded = 1;
/* StgWord is unsigned anyway, only catch 0 */
@@ -69,25 +69,6 @@ StgWord roundUp2(StgWord val) {
return rounded;
}
-INLINE_HEADER
-rtsBool casTop(StgPtr addr, StgWord old, StgWord new);
-
-#if !defined(THREADED_RTS)
-/* missing def. in non THREADED RTS, and makes no sense anyway... */
-StgWord cas(StgPtr addr,StgWord old,StgWord new);
-StgWord cas(StgPtr addr,StgWord old,StgWord new) {
- barf("cas: not implemented without multithreading");
- old = new = *addr; /* to avoid gcc warnings */
-}
-#endif
-
-INLINE_HEADER
-rtsBool casTop(StgWord* addr, StgWord old, StgWord new) {
- StgWord res = cas((StgPtr) addr, old, new);
- return ((res == old));
-}
-
-/* or simply like this */
#define CASTOP(addr,old,new) ((old) == cas(((StgPtr)addr),(old),(new)))
/* -----------------------------------------------------------------------------
@@ -97,8 +78,9 @@ rtsBool casTop(StgWord* addr, StgWord old, StgWord new) {
* -------------------------------------------------------------------------- */
/* constructor */
-SparkPool* initPool(StgWord size) {
-
+static SparkPool*
+initPool(StgWord size)
+{
StgWord realsize;
SparkPool *q;
@@ -136,14 +118,17 @@ initSparkPools( void )
}
void
-freeSparkPool(SparkPool *pool) {
+freeSparkPool (SparkPool *pool)
+{
/* should not interfere with concurrent findSpark() calls! And
nobody should use the pointer any more. We cross our fingers...*/
stgFree(pool->elements);
stgFree(pool);
}
-/* reclaimSpark(cap): remove a spark from the write end of the queue.
+/* -----------------------------------------------------------------------------
+ *
+ * reclaimSpark: remove a spark from the write end of the queue.
* Returns the removed spark, and NULL if a race is lost or the pool
* empty.
*
@@ -151,9 +136,12 @@ freeSparkPool(SparkPool *pool) {
* concurrently stealing threads by using cas to modify the top field.
* This routine should NEVER be called by a task which does not own
* the capability. Can this be checked here?
- */
-StgClosure* reclaimSpark(Capability *cap) {
- SparkPool *deque = cap->sparks;
+ *
+ * -------------------------------------------------------------------------- */
+
+StgClosure *
+reclaimSpark (SparkPool *deque)
+{
/* also a bit tricky, has to avoid concurrent steal() calls by
accessing top with cas, when there is only one element left */
StgWord t, b;
@@ -196,19 +184,17 @@ StgClosure* reclaimSpark(Capability *cap) {
/* -----------------------------------------------------------------------------
*
- * findSpark: find a spark on the current Capability that we can fork
- * into a thread.
+ * tryStealSpark: try to steal a spark from a Capability.
*
- * May be called by concurrent threads, which synchronise on top
- * variable. Returns a spark, or NULL if pool empty or race lost.
+ * Returns a valid spark, or NULL if the pool was empty, and can
+ * occasionally return NULL if there was a race with another thread
+ * stealing from the same pool. In this case, try again later.
*
-------------------------------------------------------------------------- */
-StgClosurePtr steal(SparkPool *deque);
-
-/* steal an element from the read end. Synchronises multiple callers
- by failing with NULL return. Returns NULL when deque is empty. */
-StgClosurePtr steal(SparkPool *deque) {
+static StgClosurePtr
+steal(SparkPool *deque)
+{
StgClosurePtr* pos;
StgClosurePtr* arraybase;
StgWord sz;
@@ -231,43 +217,39 @@ StgClosurePtr steal(SparkPool *deque) {
/* now decide whether we have won */
if ( !(CASTOP(&(deque->top),t,t+1)) ) {
- /* lost the race, someon else has changed top in the meantime */
- stolen = NULL;
+ /* lost the race, someon else has changed top in the meantime */
+ return NULL;
} /* else: OK, top has been incremented by the cas call */
-
ASSERT_SPARK_POOL_INVARIANTS(deque);
- /* return NULL or stolen element */
+ /* return stolen element */
return stolen;
}
StgClosure *
-findSpark (Capability *cap)
+tryStealSpark (SparkPool *pool)
{
- SparkPool *deque = (cap->sparks);
StgClosure *stolen;
- ASSERT_SPARK_POOL_INVARIANTS(deque);
-
do {
- /* keep trying until good spark found or pool looks empty.
- TODO is this a good idea? */
-
- stolen = steal(deque);
-
- } while ( ( !stolen /* nothing stolen*/
- || !closure_SHOULD_SPARK(stolen)) /* spark not OK */
- && !looksEmpty(deque)); /* run empty, give up */
+ stolen = steal(pool);
+ } while (stolen != NULL && !closure_SHOULD_SPARK(stolen));
- /* return stolen element */
return stolen;
}
-/* "guesses" whether a deque is empty. Can return false negatives in
- presence of concurrent steal() calls, and false positives in
- presence of a concurrent pushBottom().*/
-rtsBool looksEmpty(SparkPool* deque) {
+/* -----------------------------------------------------------------------------
+ *
+ * "guesses" whether a deque is empty. Can return false negatives in
+ * presence of concurrent steal() calls, and false positives in
+ * presence of a concurrent pushBottom().
+ *
+ * -------------------------------------------------------------------------- */
+
+rtsBool
+looksEmpty(SparkPool* deque)
+{
StgWord t = deque->top;
StgWord b = deque->bottom;
/* try to prefer false negatives by reading top first */
@@ -288,6 +270,7 @@ createSparkThread (Capability *cap, StgClosure *p)
tso = createGenThread (cap, RtsFlags.GcFlags.initialStkSize, p);
appendToRunQueue(cap,tso);
+ cap->sparks_converted++;
}
/* -----------------------------------------------------------------------------
@@ -297,11 +280,12 @@ createSparkThread (Capability *cap, StgClosure *p)
* -------------------------------------------------------------------------- */
#define DISCARD_NEW
-void pushBottom(SparkPool* deque, StgClosurePtr elem);
/* enqueue an element. Should always succeed by resizing the array
(not implemented yet, silently fails in that case). */
-void pushBottom(SparkPool* deque, StgClosurePtr elem) {
+static void
+pushBottom (SparkPool* deque, StgClosurePtr elem)
+{
StgWord t;
StgClosurePtr* pos;
StgWord sz = deque->moduloSize;
@@ -349,12 +333,16 @@ void pushBottom(SparkPool* deque, StgClosurePtr elem) {
}
-/* this is called as a direct C-call from Stg => we need to keep the
- pool in a register (???) */
+/* --------------------------------------------------------------------------
+ * newSpark: create a new spark, as a result of calling "par"
+ * Called directly from STG.
+ * -------------------------------------------------------------------------- */
+
StgInt
newSpark (StgRegTable *reg, StgClosure *p)
{
- SparkPool *pool = (reg->rCurrentTSO->cap->sparks);
+ Capability *cap = regTableToCapability(reg);
+ SparkPool *pool = cap->sparks;
/* I am not sure whether this is the right thing to do.
* Maybe it is better to exploit the tag information
@@ -368,6 +356,8 @@ newSpark (StgRegTable *reg, StgClosure *p)
pushBottom(pool,p);
}
+ cap->sparks_created++;
+
ASSERT_SPARK_POOL_INVARIANTS(pool);
return 1;
}
@@ -385,7 +375,7 @@ static void
pruneSparkQueue (Capability *cap)
{
SparkPool *pool;
- StgClosurePtr spark, evacspark, *elements;
+ StgClosurePtr spark, *elements;
nat n, pruned_sparks; // stats only
StgWord botInd,oldBotInd,currInd; // indices in array (always < size)
@@ -457,6 +447,7 @@ pruneSparkQueue (Capability *cap)
n++;
} else {
pruned_sparks++; // discard spark
+ cap->sparks_pruned++;
}
currInd++;
@@ -528,7 +519,6 @@ traverseSparkQueue (evac_fn evac, void *user, Capability *cap)
}
/* ----------------------------------------------------------------------------
-
* balanceSparkPoolsCaps: takes an array of capabilities (usually: all
* capabilities) and its size. Accesses all spark pools and equally
* distributes the sparks among them.
@@ -537,7 +527,8 @@ traverseSparkQueue (evac_fn evac, void *user, Capability *cap)
* -------------------------------------------------------------------------- */
void balanceSparkPoolsCaps(nat n_caps, Capability caps[]);
-void balanceSparkPoolsCaps(nat n_caps, Capability caps[]) {
+void balanceSparkPoolsCaps(nat n_caps STG_UNUSED,
+ Capability caps[] STG_UNUSED) {
barf("not implemented");
}