Safe Haskell | None |
---|

Standard combinators for distributed types.

- data Gang
- gangSize :: Gang -> Int
- seqGang :: Gang -> Gang
- forkGang :: Int -> IO Gang
- theGang :: Gang
- class DT a where
- data Dist a
- data MDist a :: * -> *
- indexD :: String -> Dist a -> Int -> a
- newMD :: Gang -> ST s (MDist a s)
- readMD :: MDist a s -> Int -> ST s a
- writeMD :: MDist a s -> Int -> a -> ST s ()
- unsafeFreezeMD :: MDist a s -> ST s (Dist a)
- deepSeqD :: a -> b -> b
- sizeD :: Dist a -> Int
- sizeMD :: MDist a s -> Int
- measureD :: a -> String

- newD :: DT a => Gang -> (forall s. MDist a s -> ST s ()) -> Dist a
- debugD :: DT a => Dist a -> String
- checkGangD :: DT a => String -> Gang -> Dist a -> b -> b
- generateD :: DT a => What -> Gang -> (Int -> a) -> Dist a
- generateD_cheap :: DT a => What -> Gang -> (Int -> a) -> Dist a
- imapD' :: (DT a, DT b) => What -> Gang -> (Int -> a -> b) -> Dist a -> Dist b
- foldD :: DT a => What -> Gang -> (a -> a -> a) -> Dist a -> a
- scanD :: forall a. DT a => What -> Gang -> (a -> a -> a) -> a -> Dist a -> (Dist a, a)

# The Gang

DPH programs use this single, shared gang of threads. The gang exists at top level, and is initialised at program start.

The vectoriser guarantees that the gang is only used by a single computation at a time. This is true because the program produced by the vector only uses flat parallelism, so parallel computations don't invoke further parallel computations. If the vectorised program tries to use nested parallelism then there is a bug in the vectoriser, and the code will run sequentially.

# Distributed Types

Class of distributable types. Instances of `DT`

can be
distributed across all workers of a `Gang`

.
All such types must be hyperstrict as we do not want to pass thunks
into distributed computations.

indexD :: String -> Dist a -> Int -> aSource

Extract a single element of an immutable distributed value.

newMD :: Gang -> ST s (MDist a s)Source

Create an unitialised distributed value for the given `Gang`

.
The gang is used (only) to know how many elements are needed
in the distributed value.

readMD :: MDist a s -> Int -> ST s aSource

Extract an element from a mutable distributed value.

writeMD :: MDist a s -> Int -> a -> ST s ()Source

Write an element of a mutable distributed value.

unsafeFreezeMD :: MDist a s -> ST s (Dist a)Source

Unsafely freeze a mutable distributed value.

Ensure a distributed value is fully evaluated.

Number of elements in the distributed value.

- For debugging only, as code shouldn't be sensitive to the return value.

sizeMD :: MDist a s -> IntSource

Number of elements in the mutable distributed value.

- For debugging only, as code shouldn't be sensitive to the return value.

Show a distributed value.

- For debugging only.

newD :: DT a => Gang -> (forall s. MDist a s -> ST s ()) -> Dist aSource

Given a computation that can write its result to a mutable distributed value, run the computation to generate an immutable distributed value.

checkGangD :: DT a => String -> Gang -> Dist a -> b -> bSource

Check that the sizes of the `Gang`

and of the distributed value match.

# Primitive Distributed Operators.

Create a distributed value, given a function to create the instance for each thread.

Create a distributed value, but do it sequentially.

This function is used when we want to operate on a distributed value, but there isn't much data involved. For example, if we want to distribute a single integer to each thread, then there's no need to fire up the gang for this.

imapD' :: (DT a, DT b) => What -> Gang -> (Int -> a -> b) -> Dist a -> Dist bSource

Map a function across all elements of a distributed value. The worker function also gets the current thread index.