repa-2.1.0.1: Data/Array/Repa/Internals/Forcing.hs
{-# LANGUAGE BangPatterns #-}
module Data.Array.Repa.Internals.Forcing
( toVector
, toList
, force, force2)
where
import Data.Array.Repa.Internals.EvalChunked
import Data.Array.Repa.Internals.EvalCursored
import Data.Array.Repa.Internals.Elt
import Data.Array.Repa.Internals.Base
import Data.Array.Repa.Index
import Data.Array.Repa.Shape as S
import qualified Data.Vector.Unboxed as V
import qualified Data.Vector.Unboxed.Mutable as VM
import Data.Vector.Unboxed (Vector)
import System.IO.Unsafe
stage = "Data.Array.Repa.Internals.Forcing"
-- Conversions that also force the array ----------------------------------------------------------
-- | Convert an array to an unboxed `Data.Vector`, forcing it if required.
-- The elements come out in row-major order.
toVector
:: (Shape sh, Elt a)
=> Array sh a
-> Vector a
{-# INLINE toVector #-}
toVector arr
= case force arr of
Array _ [Region _ (GenManifest vec)] -> vec
_ -> error $ stage ++ ".toVector: force failed"
-- | Convert an array to a list, forcing it if required.
toList :: (Shape sh, Elt a)
=> Array sh a
-> [a]
{-# INLINE toList #-}
toList arr
= V.toList $ toVector arr
-- Forcing ----------------------------------------------------------------------------------------
-- | Force an array, so that it becomes `Manifest`.
-- The array is split into linear chunks and each chunk evaluated in parallel.
force :: (Shape sh, Elt a)
=> Array sh a -> Array sh a
{-# INLINE [2] force #-}
force arr
= unsafePerformIO
$ do (sh, vec) <- forceIO arr
return $ sh `seq` vec `seq`
Array sh [Region RangeAll (GenManifest vec)]
where forceIO arr'
= case arr' of
-- Don't force an already forced array.
Array sh [Region RangeAll (GenManifest vec)]
-> return (sh, vec)
Array sh _
-> do mvec <- VM.unsafeNew (S.size sh)
fillChunkedP mvec (\ix -> arr' `unsafeIndex` fromIndex sh ix)
vec <- V.unsafeFreeze mvec
return (sh, vec)
-- | Force an array, so that it becomes `Manifest`.
-- This forcing function is specialised for DIM2 arrays, and does blockwise filling.
force2 :: Elt a => Array DIM2 a -> Array DIM2 a
{-# INLINE [2] force2 #-}
force2 arr
= unsafePerformIO
$ do (sh, vec) <- forceIO2 arr
return $ sh `seq` vec `seq`
Array sh [Region RangeAll (GenManifest vec)]
where forceIO2 arr'
= arr' `deepSeqArray`
case arr' of
-- Don't force an already forced array.
Array sh [Region RangeAll (GenManifest vec)]
-> return (sh, vec)
-- Create a vector to hold the new array and load in the regions.
-- NOTE We must specialise this for the common case of two regions to enable
-- fusion for them. If we just have the next case (arbitrary region list)
-- the worker won't fuse with the filling / evaluation code.
Array sh [r1]
-> do mvec <- VM.new (S.size sh)
fillRegion2P mvec sh r1
vec <- V.unsafeFreeze mvec
return (sh, vec)
Array sh [r1, r2]
-> do mvec <- VM.new (S.size sh)
fillRegion2P mvec sh r1
fillRegion2P mvec sh r2
vec <- V.unsafeFreeze mvec
return (sh, vec)
-- Create a vector to hold the new array and load in the regions.
Array sh regions
-> do mvec <- VM.new (S.size sh)
mapM_ (fillRegion2P mvec sh) regions
vec <- V.unsafeFreeze mvec
return (sh, vec)
-- FillRegion2P -----------------------------------------------------------------------------------
-- | Fill an array region into a vector.
-- This is specialised for DIM2 regions.
-- The region is evaluated in parallel in a blockwise manner, where each block is
-- evaluated independently and in a separate thread. For delayed or cursored regions
-- access their source elements from the local neighbourhood, this specialised version
-- should given better cache performance than plain `fillRegionP`.
--
fillRegion2P
:: Elt a
=> VM.IOVector a -- ^ Vector to write elements into.
-> DIM2 -- ^ Extent of entire array.
-> Region DIM2 a -- ^ Region to fill.
-> IO ()
{-# INLINE [1] fillRegion2P #-}
fillRegion2P mvec sh@(_ :. height :. width) (Region range gen)
= mvec `seq` height `seq` width `seq`
case range of
RangeAll
-> fillRect2 mvec sh gen
(Rect (Z :. 0 :. 0)
(Z :. height - 1 :. width - 1))
RangeRects _ [rect]
-> fillRect2 mvec sh gen rect
-- Specialise for the common case of 4 rectangles so we get fusion.
-- The following case with mapM_ doesn't fuse because mapM_ isn't completely unrolled.
RangeRects _ [r1, r2, r3, r4]
-> do fillRect2 mvec sh gen r1
fillRect2 mvec sh gen r2
fillRect2 mvec sh gen r3
fillRect2 mvec sh gen r4
RangeRects _ rects
-> mapM_ (fillRect2 mvec sh gen) rects
-- | Fill a rectangle in a vector.
fillRect2
:: Elt a
=> VM.IOVector a -- ^ Vector to write elements into.
-> DIM2 -- ^ Extent of entire array.
-> Generator DIM2 a -- ^ Generator for array elements.
-> Rect DIM2 -- ^ Rectangle to fill.
-> IO ()
{-# INLINE fillRect2 #-}
fillRect2 mvec (_ :. _ :. width) gen (Rect (Z :. y0 :. x0) (Z :. y1 :. x1))
= mvec `seq` width `seq` y0 `seq` x0 `seq` y1 `seq` x1 `seq`
case gen of
GenManifest{}
-> error "fillRegion2P: GenManifest, copy elements."
-- Cursor based arrays.
GenCursor makeCursor shiftCursor loadElem
-> fillCursoredBlock2P mvec
makeCursor shiftCursor loadElem
width x0 y0 x1 y1