Permalink
Browse files

[project @ 2005-11-18 15:24:12 by simonmar]

Two improvements to the SMP runtime:

  - support for 'par', aka sparks.  Load balancing is very primitive
    right now, but I have seen programs that go faster using par.

  - support for backing off when a thread is found to be duplicating
    a computation currently underway in another thread.  This also
    fixes some instability in SMP, because it turned out that when
    an update frame points to an indirection, which can happen if
    a thunk is under evaluation in multiple threads, then after GC
    has shorted out the indirection the update will trash the value.
    Now we suspend the duplicate computation to the heap before this
    can happen.

Additionally:

  - stack squeezing is separate from lazy blackholing, and now only
    happens if there's a reasonable amount of squeezing to be done
    in relation to the number of words of stack that have to be moved.
    This means we won't try to shift 10Mb of stack just to save 2
    words at the bottom (it probably never happened, but still).

  - update frames are now marked when they have been visited by lazy
    blackholing, as per the SMP paper.

  - cleaned up raiseAsync() a bit.
  • Loading branch information...
1 parent 6c17d62 commit c5cd2343c5a86c8cb5349823a9699b30a269f3e8 simonmar committed Nov 18, 2005
View
@@ -339,6 +339,11 @@ typedef struct {
* - In StgTRecHeader, it might be worthwhile having separate chunks
* of read-only and read-write locations. This would save a
* new_value field in the read-only locations.
+ *
+ * - In StgAtomicallyFrame, we could combine the waiting bit into
+ * the header (maybe a different info tbl for a waiting transaction).
+ * This means we can specialise the code for the atomically frame
+ * (it immediately switches on frame->waiting anyway).
*/
typedef struct StgTVarWaitQueue_ {
View
@@ -24,22 +24,30 @@
#include "gmp.h" // Needs MP_INT definition
-/*
- * This is the table that holds shadow-locations for all the STG
- * registers. The shadow locations are used when:
- *
- * 1) the particular register isn't mapped to a real machine
- * register, probably because there's a shortage of real registers.
- * 2) caller-saves registers are saved across a CCall
+/*
+ * Spark pools: used to store pending sparks (SMP & PARALLEL_HASKELL only)
+ * This is a circular buffer. Invariants:
+ * - base <= hd < lim
+ * - base <= tl < lim
+ * - if hd==tl, then the pool is empty.
+ * - if hd == tl+1, then the pool is full.
+ * Adding to the pool is done by assigning to *tl++ (wrapping round as
+ * necessary). When adding to a full pool, we have the option of
+ * throwing away either the oldest (hd++) or the most recent (tl--) entry.
*/
-
typedef struct StgSparkPool_ {
StgClosure **base;
StgClosure **lim;
StgClosure **hd;
StgClosure **tl;
} StgSparkPool;
+#define ASSERT_SPARK_POOL_INVARIANTS(p) \
+ ASSERT((p)->base <= (p)->hd); \
+ ASSERT((p)->hd < (p)->lim); \
+ ASSERT((p)->base <= (p)->tl); \
+ ASSERT((p)->tl < (p)->lim);
+
typedef struct {
StgFunPtr stgGCEnter1;
StgFunPtr stgGCFun;
@@ -64,6 +72,14 @@ typedef union {
StgTSOPtr t;
} StgUnion;
+/*
+ * This is the table that holds shadow-locations for all the STG
+ * registers. The shadow locations are used when:
+ *
+ * 1) the particular register isn't mapped to a real machine
+ * register, probably because there's a shortage of real registers.
+ * 2) caller-saves registers are saved across a CCall
+ */
typedef struct StgRegTable_ {
StgUnion rR1;
StgUnion rR2;
View
@@ -62,6 +62,7 @@ struct DEBUG_FLAGS {
rtsBool linker; /* 'l' the object linker */
rtsBool apply; /* 'a' */
rtsBool stm; /* 'm' */
+ rtsBool squeeze; /* 'z' stack squeezing & lazy blackholing */
};
struct COST_CENTRE_FLAGS {
@@ -37,6 +37,7 @@
/* Stack frames */
RTS_RET_INFO(stg_upd_frame_info);
+RTS_RET_INFO(stg_marked_upd_frame_info);
RTS_RET_INFO(stg_noupd_frame_info);
RTS_RET_INFO(stg_seq_frame_info);
RTS_RET_INFO(stg_catch_frame_info);
@@ -45,6 +46,7 @@ RTS_RET_INFO(stg_atomically_frame_info);
RTS_RET_INFO(stg_catch_stm_frame_info);
RTS_ENTRY(stg_upd_frame_ret);
+RTS_ENTRY(stg_marked_upd_frame_ret);
RTS_ENTRY(stg_seq_frame_ret);
/* Entry code for constructors created by the bytecode interpreter */
View
@@ -392,7 +392,7 @@ extern lnat countNurseryBlocks ( void );
Functions from GC.c
-------------------------------------------------------------------------- */
-extern void threadPaused ( StgTSO * );
+extern void threadPaused ( Capability *cap, StgTSO * );
extern StgClosure * isAlive ( StgClosure *p );
extern void markCAFs ( evac_fn evac );
View
@@ -23,6 +23,7 @@
#include "OSThreads.h"
#include "Capability.h"
#include "Schedule.h"
+#include "Sparks.h"
#if !defined(SMP)
Capability MainCapability; // for non-SMP, we have one global capability
@@ -74,6 +75,8 @@ anyWorkForMe( Capability *cap, Task *task )
} else {
return (cap->run_queue_hd->bound == task);
}
+ } else if (task->tso == NULL && !emptySparkPoolCap(cap)) {
+ return rtsTrue;
}
return globalWorkToDo();
}
@@ -263,7 +266,7 @@ releaseCapability_ (Capability* cap)
// If we have an unbound thread on the run queue, or if there's
// anything else to do, give the Capability to a worker thread.
- if (!emptyRunQueue(cap) || globalWorkToDo()) {
+ if (!emptyRunQueue(cap) || !emptySparkPoolCap(cap) || globalWorkToDo()) {
if (cap->spare_workers) {
giveCapabilityToTask(cap,cap->spare_workers);
// The worker Task pops itself from the queue;
Oops, something went wrong.

0 comments on commit c5cd234

Please sign in to comment.