@@ -35,6 +35,7 @@ module Streamly.Internal.Data.Array.Mut.Type
39 -- * Constructing and Writing
40 -- * Constructing and Writing
40 -- ** Construction
41 -- ** Construction
@@ -48,15 +49,17 @@ module Streamly.Internal.Data.Array.Mut.Type
48 , newArrayWith
49 , newArrayWith
50 -- *** Initialized Arrays
51 -- *** Initialized Arrays
51− , withNewArrayUnsafe
52+ , withNewArrayUnsafePinned
53 -- *** From streams
54 -- *** From streams
54 , ArrayUnsafe (..)
55 , ArrayUnsafe (..)
55 , writeNWithUnsafe
56 , writeNWithUnsafe
56 , writeNWith
57 , writeNWith
57 , writeNUnsafe
58 , writeNUnsafe
59− , writeNAligned
61+ , writeNAs
62+ , pinnedWriteNAligned
61 , writeWith
64 , writeWith
@@ -185,6 +188,7 @@ module Streamly.Internal.Data.Array.Mut.Type
186 -- ** Construct from streams
189 -- ** Construct from streams
187 , chunksOf
190 , chunksOf
188 , arrayStreamKFromStreamD
192 , arrayStreamKFromStreamD
189 , writeChunks
193 , writeChunks
@@ -236,6 +240,7 @@ import Foreign.Ptr (plusPtr, minusPtr, nullPtr)
236import Streamly.Internal.Data.Unbox
240import Streamly.Internal.Data.Unbox
237 ( MutableByteArray(..)
241 ( MutableByteArray(..)
238 , Unbox(..)
242 , Unbox(..)
239 , getMutableByteArray#
244 , getMutableByteArray#
@@ -366,6 +371,10 @@ unpin arr@MutArray{..} = do
366 contents <- Unboxed.unpin arrContents
371 contents <- Unboxed.unpin arrContents
367 return $ arr {arrContents = contents}
372 return $ arr {arrContents = contents}
374+{-# INLINE isPinned #-}
375+isPinned :: MutArray a -> Bool
376+isPinned MutArray{..} = Unboxed.isPinned arrContents
369-------------------------------------------------------------------------------
378-------------------------------------------------------------------------------
370-- Construction
379-- Construction
371-------------------------------------------------------------------------------
380-------------------------------------------------------------------------------
@@ -412,6 +421,20 @@ nil ::
412 MutArray a
421 MutArray a
413nil = MutArray Unboxed.nil 0 0 0
422nil = MutArray Unboxed.nil 0 0 0
424+{-# INLINE newBytesAs #-}
425+newBytesAs :: MonadIO m =>
429+ PinnedState -> Int -> m (MutArray a)
430+newBytesAs ps bytes = do
431+ contents <- liftIO $ Unboxed.newBytes ps bytes
433+ { arrContents = contents
416-- | Allocates a pinned empty array that can hold 'count' items. The memory of
439-- | Allocates a pinned empty array that can hold 'count' items. The memory of
417-- the array is uninitialized and the allocation is aligned as per the
440-- the array is uninitialized and the allocation is aligned as per the
@@ -424,14 +447,7 @@ newPinnedBytes :: MonadIO m =>
424 Unbox a =>
447 Unbox a =>
426 Int -> m (MutArray a)
449 Int -> m (MutArray a)
427−newPinnedBytes bytes = do
450+newPinnedBytes = newBytesAs Pinned
428− contents <- liftIO $ Unboxed.newPinnedBytes bytes
430− { arrContents = contents
436-- | Like 'newArrayWith' but using an allocator is a pinned memory allocator and
452-- | Like 'newArrayWith' but using an allocator is a pinned memory allocator and
437-- the alignment is dictated by the 'Unboxed' instance of the type.
453-- the alignment is dictated by the 'Unboxed' instance of the type.
@@ -442,6 +458,13 @@ newAlignedPinned :: (MonadIO m, Unbox a) => Int -> Int -> m (MutArray a)
442newAlignedPinned =
458newAlignedPinned =
443 newArrayWith (\s a -> liftIO $ Unboxed.newAlignedPinnedBytes s a)
459 newArrayWith (\s a -> liftIO $ Unboxed.newAlignedPinnedBytes s a)
462+newAs :: (MonadIO m, Unbox a) => PinnedState -> Int -> m (MutArray a)
465+ (\s _ -> liftIO $ Unboxed.newBytes ps s)
466+ (error "new: alignment is not used in unpinned arrays.")
445-- XXX can unaligned allocation be more efficient when alignment is not needed?
468-- XXX can unaligned allocation be more efficient when alignment is not needed?
447-- | Allocates an empty pinned array that can hold 'count' items. The memory of
470-- | Allocates an empty pinned array that can hold 'count' items. The memory of
@@ -450,20 +473,14 @@ newAlignedPinned =
451{-# INLINE newPinned #-}
474{-# INLINE newPinned #-}
452newPinned :: forall m a. (MonadIO m, Unbox a) => Int -> m (MutArray a)
475newPinned :: forall m a. (MonadIO m, Unbox a) => Int -> m (MutArray a)
453−newPinned =
476+newPinned = newAs Pinned
455− (\s _ -> liftIO $ Unboxed.newPinnedBytes s)
456− (error "newPinned: alignSize is not used")
458-- | Allocates an empty unpinned array that can hold 'count' items. The memory
478-- | Allocates an empty unpinned array that can hold 'count' items. The memory
459-- of the array is uninitialized.
479-- of the array is uninitialized.
461{-# INLINE new #-}
481{-# INLINE new #-}
462new :: (MonadIO m, Unbox a) => Int -> m (MutArray a)
482new :: (MonadIO m, Unbox a) => Int -> m (MutArray a)
463−new =
483+new = newAs Unpinned
465− (\s _ -> liftIO $ Unboxed.newUnpinnedBytes s)
466− (error "new: alignment is not used in unpinned arrays.")
468-- XXX This should create a full length uninitialzed array so that the pointer
485-- XXX This should create a full length uninitialzed array so that the pointer
469-- can be used.
486-- can be used.
@@ -472,10 +489,10 @@ new =
472-- the array start pointer.
489-- the array start pointer.
474-- /Internal/
491-- /Internal/
475−{-# INLINE withNewArrayUnsafe #-}
492+{-# INLINE withNewArrayUnsafePinned #-}
476−withNewArrayUnsafe ::
493+withNewArrayUnsafePinned ::
477 (MonadIO m, Unbox a) => Int -> (Ptr a -> m ()) -> m (MutArray a)
494 (MonadIO m, Unbox a) => Int -> (Ptr a -> m ()) -> m (MutArray a)
478−withNewArrayUnsafe count f = do
495+withNewArrayUnsafePinned count f = do
479 arr <- newPinned count
496 arr <- newPinned count
480 asPtrUnsafe arr
497 asPtrUnsafe arr
481 $ \p -> f p >> return arr
498 $ \p -> f p >> return arr
@@ -736,7 +753,10 @@ reallocExplicit elemSize newCapacityInBytes MutArray{..} = do
737 -- Allocate new array
754 -- Allocate new array
738 let newCapMaxInBytes = roundUpLargeArray newCapacityInBytes
755 let newCapMaxInBytes = roundUpLargeArray newCapacityInBytes
757+ if Unboxed.isPinned arrContents
739− contents <- Unboxed.newPinnedBytes newCapMaxInBytes
758+ then Unboxed.newPinnedBytes newCapMaxInBytes
759+ else Unboxed.newUnpinnedBytes newCapMaxInBytes
740 let !(MutableByteArray mbarrFrom#) = arrContents
760 let !(MutableByteArray mbarrFrom#) = arrContents
741 !(MutableByteArray mbarrTo#) = contents
761 !(MutableByteArray mbarrTo#) = contents
@@ -1299,20 +1319,10 @@ data GroupState s contents start end bound
1299 contents start end bound (GroupState s contents start end bound)
1319 contents start end bound (GroupState s contents start end bound)
1300 | GroupFinish
1320 | GroupFinish
1302−-- | @chunksOf n stream@ groups the input stream into a stream of
1303−-- arrays of size n.
1305−-- @chunksOf n = StreamD.foldMany (MutArray.writeN n)@
1308−{-# INLINE_NORMAL chunksOf #-}
1322+{-# INLINE_NORMAL chunksOfAs #-}
1309−chunksOf :: forall m a. (MonadIO m, Unbox a)
1323+chunksOfAs :: forall m a. (MonadIO m, Unbox a)
1310− => Int -> D.Stream m a -> D.Stream m (MutArray a)
1324+ => PinnedState -> Int -> D.Stream m a -> D.Stream m (MutArray a)
1311−-- XXX the idiomatic implementation leads to large regression in the D.reverse'
1312−-- benchmark. It seems it has difficulty producing optimized code when
1313−-- converting to StreamK. Investigate GHC optimizations.
1314−-- chunksOf n = D.foldMany (writeN n)
1315−chunksOf n (D.Stream step state) =
1325+chunksOfAs ps n (D.Stream step state) =
1316 D.Stream step' (GroupStart state)
1326 D.Stream step' (GroupStart state)
@@ -1324,7 +1334,7 @@ chunksOf n (D.Stream step state) =
1324 error $ "Streamly.Internal.Data.MutArray.Mut.Type.chunksOf: "
1334 error $ "Streamly.Internal.Data.MutArray.Mut.Type.chunksOf: "
1325 ++ "the size of arrays [" ++ show n
1335 ++ "the size of arrays [" ++ show n
1326 ++ "] must be a natural number"
1336 ++ "] must be a natural number"
1327− (MutArray contents start end bound :: MutArray a) <- liftIO $ newPinned n
1337+ (MutArray contents start end bound :: MutArray a) <- newAs ps n
1328 return $ D.Skip (GroupBuffer st contents start end bound)
1338 return $ D.Skip (GroupBuffer st contents start end bound)
1330 step' gst (GroupBuffer st contents start end bound) = do
1340 step' gst (GroupBuffer st contents start end bound) = do
@@ -1350,6 +1360,22 @@ chunksOf n (D.Stream step state) =
1351 step' _ GroupFinish = return D.Stop
1361 step' _ GroupFinish = return D.Stop
1363+-- | @chunksOf n stream@ groups the input stream into a stream of
1364+-- arrays of size n.
1366+-- @chunksOf n = StreamD.foldMany (MutArray.writeN n)@
1369+{-# INLINE_NORMAL chunksOf #-}
1370+chunksOf :: forall m a. (MonadIO m, Unbox a)
1371+ => Int -> D.Stream m a -> D.Stream m (MutArray a)
1372+-- XXX the idiomatic implementation leads to large regression in the D.reverse'
1373+-- benchmark. It seems it has difficulty producing optimized code when
1374+-- converting to StreamK. Investigate GHC optimizations.
1375+-- chunksOf n = D.foldMany (writeN n)
1376+chunksOf = chunksOfAs Unpinned
1378+-- XXX This should take a PinnedState
1353-- XXX buffer to a list instead?
1379-- XXX buffer to a list instead?
1354-- | Buffer the stream into arrays in memory.
1380-- | Buffer the stream into arrays in memory.
1355{-# INLINE arrayStreamKFromStreamD #-}
1381{-# INLINE arrayStreamKFromStreamD #-}
@@ -1751,18 +1777,23 @@ writeNWithUnsafe alloc n = fromArrayUnsafe <$> FL.foldlM' step initial
1752 $ ArrayUnsafe contents start (INDEX_NEXT(end,a))
1778 $ ArrayUnsafe contents start (INDEX_NEXT(end,a))
1780+{-# INLINE_NORMAL writeNUnsafeAs #-}
1781+writeNUnsafeAs :: forall m a. (MonadIO m, Unbox a)
1782+ => PinnedState -> Int -> Fold m a (MutArray a)
1783+writeNUnsafeAs ps = writeNWithUnsafe (newAs ps)
1754-- | Like 'writeN' but does not check the array bounds when writing. The fold
1785-- | Like 'writeN' but does not check the array bounds when writing. The fold
1755-- driver must not call the step function more than 'n' times otherwise it will
1786-- driver must not call the step function more than 'n' times otherwise it will
1756-- corrupt the memory and crash. This function exists mainly because any
1787-- corrupt the memory and crash. This function exists mainly because any
1757-- conditional in the step function blocks fusion causing 10x performance
1788-- conditional in the step function blocks fusion causing 10x performance
1758-- slowdown.
1789-- slowdown.
1760−-- >>> writeNUnsafe = MutArray.writeNWithUnsafe MutArray.newPinned
1791+-- >>> writeNUnsafe = MutArray.writeNWithUnsafe MutArray.new
1762{-# INLINE_NORMAL writeNUnsafe #-}
1793{-# INLINE_NORMAL writeNUnsafe #-}
1763writeNUnsafe :: forall m a. (MonadIO m, Unbox a)
1794writeNUnsafe :: forall m a. (MonadIO m, Unbox a)
1764 => Int -> Fold m a (MutArray a)
1795 => Int -> Fold m a (MutArray a)
1765−writeNUnsafe = writeNWithUnsafe newPinned
1796+writeNUnsafe = writeNUnsafeAs Unpinned
1767-- | @writeNWith alloc n@ folds a maximum of @n@ elements into an array
1798-- | @writeNWith alloc n@ folds a maximum of @n@ elements into an array
1768-- allocated using the @alloc@ function.
1799-- allocated using the @alloc@ function.
@@ -1775,16 +1806,24 @@ writeNWith :: forall m a. (MonadIO m, Unbox a)
1775 => (Int -> m (MutArray a)) -> Int -> Fold m a (MutArray a)
1806 => (Int -> m (MutArray a)) -> Int -> Fold m a (MutArray a)
1776writeNWith alloc n = FL.take n (writeNWithUnsafe alloc n)
1807writeNWith alloc n = FL.take n (writeNWithUnsafe alloc n)
1809+{-# INLINE_NORMAL writeNAs #-}
1811+ forall m a. (MonadIO m, Unbox a)
1814+ -> Fold m a (MutArray a)
1815+writeNAs ps = writeNWith (newAs ps)
1778-- | @writeN n@ folds a maximum of @n@ elements from the input stream to an
1817-- | @writeN n@ folds a maximum of @n@ elements from the input stream to an
1779-- 'MutArray'.
1818-- 'MutArray'.
1781−-- >>> writeN = MutArray.writeNWith MutArray.newPinned
1820+-- >>> writeN = MutArray.writeNWith MutArray.new
1782-- >>> writeN n = Fold.take n (MutArray.writeNUnsafe n)
1821-- >>> writeN n = Fold.take n (MutArray.writeNUnsafe n)
1783−-- >>> writeN n = MutArray.writeAppendN n (MutArray.newPinned n)
1822+-- >>> writeN n = MutArray.writeAppendN n (MutArray.new n)
1785{-# INLINE_NORMAL writeN #-}
1824{-# INLINE_NORMAL writeN #-}
1786writeN :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)
1825writeN :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)
1787−writeN = writeNWith newPinned
1826+writeN = writeNAs Unpinned
1789-- | Like writeNWithUnsafe but writes the array in reverse order.
1828-- | Like writeNWithUnsafe but writes the array in reverse order.
@@ -1820,20 +1859,20 @@ writeRevNWith alloc n = FL.take n (writeRevNWithUnsafe alloc n)
1820-- /Pre-release/
1859-- /Pre-release/
1821{-# INLINE_NORMAL writeRevN #-}
1860{-# INLINE_NORMAL writeRevN #-}
1822writeRevN :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)
1861writeRevN :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)
1823−writeRevN = writeRevNWith newPinned
1862+writeRevN = writeRevNWith new
1825−-- | @writeNAligned align n@ folds a maximum of @n@ elements from the input
1864+-- | @pinnedWriteNAligned align n@ folds a maximum of @n@ elements from the
1826−-- stream to a 'MutArray' aligned to the given size.
1865+-- input stream to a 'MutArray' aligned to the given size.
1828−-- >>> writeNAligned align = MutArray.writeNWith (MutArray.newAlignedPinned align)
1867+-- >>> pinnedWriteNAligned align = MutArray.writeNWith (MutArray.newAlignedPinned align)
1829−-- >>> writeNAligned align n = MutArray.writeAppendN n (MutArray.newAlignedPinned align n)
1868+-- >>> pinnedWriteNAligned align n = MutArray.writeAppendN n (MutArray.newAlignedPinned align n)
1831-- /Pre-release/
1870-- /Pre-release/
1833−{-# INLINE_NORMAL writeNAligned #-}
1872+{-# INLINE_NORMAL pinnedWriteNAligned #-}
1834−writeNAligned :: forall m a. (MonadIO m, Unbox a)
1873+pinnedWriteNAligned :: forall m a. (MonadIO m, Unbox a)
1835 => Int -> Int -> Fold m a (MutArray a)
1874 => Int -> Int -> Fold m a (MutArray a)
1836−writeNAligned align = writeNWith (newAlignedPinned align)
1875+pinnedWriteNAligned align = writeNWith (newAlignedPinned align)
1838-- XXX Buffer to a list instead?
1877-- XXX Buffer to a list instead?
@@ -1876,7 +1915,7 @@ writeChunks n = FL.many (writeN n) FL.toStreamK
1877-- /Caution! Do not use this on infinite streams./
1916-- /Caution! Do not use this on infinite streams./
1879−-- >>> f n = MutArray.writeAppendWith (* 2) (MutArray.newPinned n)
1918+-- >>> f n = MutArray.writeAppendWith (* 2) (MutArray.new n)
1880-- >>> writeWith n = Fold.rmapM MutArray.rightSize (f n)
1919-- >>> writeWith n = Fold.rmapM MutArray.rightSize (f n)
1881-- >>> writeWith n = Fold.rmapM MutArray.fromArrayStreamK (MutArray.writeChunks n)
1920-- >>> writeWith n = Fold.rmapM MutArray.fromArrayStreamK (MutArray.writeChunks n)
@@ -1884,7 +1923,7 @@ writeChunks n = FL.many (writeN n) FL.toStreamK
1884{-# INLINE_NORMAL writeWith #-}
1923{-# INLINE_NORMAL writeWith #-}
1885writeWith :: forall m a. (MonadIO m, Unbox a)
1924writeWith :: forall m a. (MonadIO m, Unbox a)
1886 => Int -> Fold m a (MutArray a)
1925 => Int -> Fold m a (MutArray a)
1887−-- writeWith n = FL.rmapM rightSize $ writeAppendWith (* 2) (newPinned n)
1926+-- writeWith n = FL.rmapM rightSize $ writeAppendWith (* 2) (new n)
1888writeWith elemCount =
1927writeWith elemCount =
1889 FL.rmapM extract $ FL.foldlM' step initial
1928 FL.rmapM extract $ FL.foldlM' step initial
@@ -1892,7 +1931,7 @@ writeWith elemCount =
1893 initial = do
1932 initial = do
1894 when (elemCount < 0) $ error "writeWith: elemCount is negative"
1933 when (elemCount < 0) $ error "writeWith: elemCount is negative"
1895− liftIO $ newPinned elemCount
1934+ liftIO $ new elemCount
1897 step arr@(MutArray _ start end bound) x
1936 step arr@(MutArray _ start end bound) x
1898 | INDEX_NEXT(end,a) > bound = do
1937 | INDEX_NEXT(end,a) > bound = do
@@ -1928,7 +1967,7 @@ fromStreamDN :: forall m a. (MonadIO m, Unbox a)
1928 => Int -> D.Stream m a -> m (MutArray a)
1967 => Int -> D.Stream m a -> m (MutArray a)
1929-- fromStreamDN n = D.fold (writeN n)
1968-- fromStreamDN n = D.fold (writeN n)
1930fromStreamDN limit str = do
1969fromStreamDN limit str = do
1931− (arr :: MutArray a) <- liftIO $ newPinned limit
1970+ (arr :: MutArray a) <- liftIO $ new limit
1932 end <- D.foldlM' (fwrite (arrContents arr)) (return $ arrEnd arr) $ D.take limit str
1971 end <- D.foldlM' (fwrite (arrContents arr)) (return $ arrEnd arr) $ D.take limit str
1933 return $ arr {arrEnd = end}
1972 return $ arr {arrEnd = end}
@@ -2040,7 +2079,7 @@ spliceCopy arr1 arr2 = liftIO $ do
2040 start2 = arrStart arr2
2079 start2 = arrStart arr2
2041 len1 = arrEnd arr1 - start1
2080 len1 = arrEnd arr1 - start1
2042 len2 = arrEnd arr2 - start2
2081 len2 = arrEnd arr2 - start2
2043− newArrContents <- liftIO $ Unboxed.newPinnedBytes (len1 + len2)
2082+ newArrContents <- liftIO $ Unboxed.newUnpinnedBytes (len1 + len2)
2044 let len = len1 + len2
2083 let len = len1 + len2
2045 newArr = MutArray newArrContents 0 len len
2084 newArr = MutArray newArrContents 0 len len
2046 putSliceUnsafe arr1 start1 newArr 0 len1
2085 putSliceUnsafe arr1 start1 newArr 0 len1
@@ -2251,12 +2290,10 @@ cast arr =
2252asPtrUnsafe :: MonadIO m => MutArray a -> (Ptr a -> m b) -> m b
2291asPtrUnsafe :: MonadIO m => MutArray a -> (Ptr a -> m b) -> m b
2253asPtrUnsafe arr f = do
2292asPtrUnsafe arr f = do
2254− let contents = arrContents arr
2293+ contents <- liftIO $ Unboxed.pin $ arrContents arr
2255− !ptr = Ptr (byteArrayContents#
2294+ let !ptr = Ptr (byteArrayContents#
2256 (unsafeCoerce# (getMutableByteArray# contents)))
2295 (unsafeCoerce# (getMutableByteArray# contents)))
2257− -- XXX Check if the array is pinned, if not, copy it to a pinned array
2296+
2258− -- XXX We should probably pass to the IO action the byte length of the array
2259− -- as well so that bounds can be checked.
2260 r <- f (ptr `plusPtr` arrStart arr)
2297 r <- f (ptr `plusPtr` arrStart arr)
2261 liftIO $ touch contents
2298 liftIO $ touch contents
2262 return r
2299 return r