A spark pool is the runtime-managed holding area for sparks: pointers to unevaluated thunks that may be evaluated in parallel. Creating a spark does not itself evaluate the thunk; it makes parallel work available to the runtime. section 13.4.3
Haskell execution contexts (HECs), roughly runtime-managed operating-system threads, take available sparks from a pool through work stealing. An idle HEC can steal a spark and evaluate its thunk, while the runtime handles scheduling and related cleanup details. section 13.4.3
Figure 13.5 Haskell execution contexts and spark pools
The diagram relates HECs to the spark pools from which they obtain parallel work. section 13.4.3
Putting work into the pool
`rpar x` creates a spark for `x` inside `Eval`; `runEval` retrieves the result. `return` does not wait for the spark to finish, so demand from the calling code can make the spark invalid before an HEC evaluates it—a fizzle. A spark for an already evaluated value is a dud. section 13.4.3
runEval $ do x' <- rpar x return x'
This small `Eval` computation sparks `x`, placing its possible parallel evaluation under runtime scheduling. section 13.4.3
Pool capacity matters. Making a spark for every small unit of work can overflow the pool, leaving parallelism ineffective. For a large collection, split work into larger chunks and spark those chunks instead; this reduces the number of sparks while preserving parallel work. section 13.4.3 section 13.5
Chunking an image mapping
A vector mapping strategy using `evalTraversable rpar` produced 1,318,768 sparks, including 1,210,301 overflows. A `parVectorChunk` strategy instead divides the vector into chunks and sequentially evaluates each chunk, producing 1,001 converted sparks with no overflows in the reported run. section 13.4.3
Listing 13.25 A module for chunked parallel execution of the Vector type
module Data.Vector.Strategies (parVectorChunk) where import Control.Parallel.Strategies #1 import qualified Data.Vector as V #1 parVectorChunk :: Int -> Strategy (V.Vector a) parVectorChunk size = fmap V.fromList . parListChunk size rseq . V.toList #2
`parVectorChunk` converts a vector to chunks, evaluates each chunk with `rseq`, and rebuilds the vector. section 13.4.3
