{-# LANGUAGE BangPatterns #-} module Data.Array.Repa.Internals.Forcing ( toVector , toList , force, forceWith , force2, forceWith2) 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) forceWith (VM.unsafeWrite mvec) arr' vec <- V.unsafeFreeze mvec return (sh, vec) -- | Force an array, passing elements to the provided update function. -- Provide something like @(Foreign.Ptr.pokeElemOff ptr)@ to write elements into a buffer. -- The array is split into linear chunks and each chunk is evaluated in parallel. forceWith :: (Shape sh, Elt a) => (Int -> a -> IO ()) -> Array sh a -> IO () {-# INLINE [2] forceWith #-} forceWith !update arr@(Array sh _) = fillChunkedP (S.size sh) update (\ix -> arr `unsafeIndex` fromIndex sh ix) -- | 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. Array sh _ -> do mvec <- VM.new (S.size sh) forceWith2 (VM.unsafeWrite mvec) arr' vec <- V.unsafeFreeze mvec return (sh, vec) -- | Force an array, passing elements to the provided update function. -- Provide something like @(Foreign.Ptr.pokeElemOff ptr)@ to write elements into a buffer. -- This forcing function is specialised for DIM2 arrays, and does blockwise filling. forceWith2 :: Elt a => (Int -> a -> IO ()) -> Array DIM2 a -> IO () {-# INLINE [2] forceWith2 #-} forceWith2 !write arr = arr `deepSeqArray` case arr of -- If the array is already manifest then copy it into the buffer. -- We don't need a particular traversal order just for a copy. Array _ [Region RangeAll (GenManifest _)] -> forceWith write arr -- NOTE We must specialise this for common numbers of regions so that -- we get fusion for them. If we just have the last case (arbitrary -- region list) then the worker won't fuse with the filling / -- evaluation code. Array sh [r1] -> do fillRegion2P write sh r1 Array sh [r1, r2] -> do fillRegion2P write sh r1 fillRegion2P write sh r2 Array sh regions -> do mapM_ (fillRegion2P write sh) regions -- 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 => (Int -> a -> IO ()) -- ^ Update function to write into result buffer -> DIM2 -- ^ Extent of entire array. -> Region DIM2 a -- ^ Region to fill. -> IO () {-# INLINE [1] fillRegion2P #-} fillRegion2P write sh@(_ :. height :. width) (Region range gen) = write `seq` height `seq` width `seq` case range of RangeAll -> fillRect2 write sh gen (Rect (Z :. 0 :. 0) (Z :. height - 1 :. width - 1)) RangeRects _ [r1] -> do fillRect2 write sh gen r1 RangeRects _ [r1, r2] -> do fillRect2 write sh gen r1 fillRect2 write sh gen r2 RangeRects _ [r1, r2, r3] -> do fillRect2 write sh gen r1 fillRect2 write sh gen r2 fillRect2 write sh gen r3 RangeRects _ [r1, r2, r3, r4] -> do fillRect2 write sh gen r1 fillRect2 write sh gen r2 fillRect2 write sh gen r3 fillRect2 write sh gen r4 RangeRects _ rects -> mapM_ (fillRect2 write sh gen) rects -- | Fill a rectangle in a vector. fillRect2 :: Elt a => (Int -> a -> IO ()) -- ^ Update function to write into result buffer -> DIM2 -- ^ Extent of entire array. -> Generator DIM2 a -- ^ Generator for array elements. -> Rect DIM2 -- ^ Rectangle to fill. -> IO () {-# INLINE fillRect2 #-} fillRect2 write sh@(_ :. _ :. width) gen (Rect (Z :. y0 :. x0) (Z :. y1 :. x1)) = write `seq` width `seq` y0 `seq` x0 `seq` y1 `seq` x1 `seq` case gen of GenManifest vec -> fillCursoredBlock2P write id addDim (\ix -> vec `V.unsafeIndex` toIndex sh ix) width x0 y0 x1 y1 -- Cursor based arrays. GenCursor makeCursor shiftCursor loadElem -> fillCursoredBlock2P write makeCursor shiftCursor loadElem width x0 y0 x1 y1