@@ -43,6 +43,7 @@ module Streamly.Internal.Data.Array.Foreign.Mut.Type
43 , putIndexUnsafe
43 , putIndexUnsafe
44 , snocUnsafe
44 , snocUnsafe
47 -- * Casting
48 -- * Casting
@@ -108,6 +109,7 @@ import Control.Exception (assert)
108import Control.DeepSeq (NFData(..))
109import Control.DeepSeq (NFData(..))
109import Control.Monad (when, void)
110import Control.Monad (when, void)
110import Control.Monad.IO.Class (MonadIO(..))
111import Control.Monad.IO.Class (MonadIO(..))
112+import Data.Bits ((.&.))
111import Data.Functor.Identity (runIdentity)
113import Data.Functor.Identity (runIdentity)
112#if __GLASGOW_HASKELL__ < 808
114#if __GLASGOW_HASKELL__ < 808
113import Data.Semigroup (Semigroup(..))
115import Data.Semigroup (Semigroup(..))
@@ -331,6 +333,49 @@ putIndexUnsafe Array{..} i x =
331 assert (i >= 0 && elemPtr `plusPtr` elemSize <= aEnd) (return ())
333 assert (i >= 0 && elemPtr `plusPtr` elemSize <= aEnd) (return ())
332 liftIO $ poke elemPtr x
334 liftIO $ poke elemPtr x
336+-------------------------------------------------------------------------------
338+-------------------------------------------------------------------------------
340+-- XXX Should we use bitshifts in calculations or it gets optimized by the
341+-- compiler/processor itself?
343+-- | The page or block size used by the GHC allocator. Allocator allocates at
344+-- least a block and then allocates smaller allocations from within a block.
348+-- | Allocations larger than 'largeObjectThreshold' are in multiples of block
349+-- size and are always pinned. The space beyond the end of a large object up to
350+-- the end of the block is unused.
351+largeObjectThreshold :: Int
352+largeObjectThreshold = (blockSize * 8) `div` 10
354+-- | Round up an array larger than 'largeObjectThreshold' to use the whole
356+{-# INLINE roundUpLargeArray #-}
357+roundUpLargeArray :: Int -> Int
358+roundUpLargeArray size =
359+ if size >= largeObjectThreshold
362+ (blockSize /= 0 && ((blockSize .&. (blockSize - 1)) == 0))
363+ ((size + blockSize - 1) .&. negate blockSize)
367+roundUpToPower2 :: Int -> Int
368+roundUpToPower2 = undefined
371+-- XXX remove mkChunkSize, it is not required.
372+-- | The default chunk size by which the array creation routines increase the
373+-- size of the array when the array is grown linearly.
375+{-# INLINE arrayChunkSize #-}
376+arrayChunkSize :: Int
377+arrayChunkSize = mkChunkSize 1024
334-------------------------------------------------------------------------------
379-------------------------------------------------------------------------------
336-------------------------------------------------------------------------------
381-------------------------------------------------------------------------------
@@ -357,32 +402,102 @@ snocUnsafe :: forall m a. (MonadIO m, Storable a) =>
357snocUnsafe arr@Array{..} =
402snocUnsafe arr@Array{..} =
358 snocNewEnd (aEnd `plusPtr` sizeOf (undefined :: a)) arr
403 snocNewEnd (aEnd `plusPtr` sizeOf (undefined :: a)) arr
405+reallocWith :: forall m a. (MonadIO m , Storable a) =>
411+reallocWith label allocSize reqSize arr = do
412+ let oldSize = aEnd arr `minusPtr` unsafeForeignPtrToPtr (aStart arr)
413+ newSize = allocSize oldSize
414+ safeSize = max newSize (oldSize + reqSize)
415+ rounded = roundUpLargeArray safeSize
416+ assert (newSize >= oldSize + reqSize || error badSize) (return ())
417+ assert (rounded >= safeSize) (return ())
424+ , ": new array size is less than required size "
426+ , ". Please check the sizing function passed."
429+-- NOINLINE to move it out of the way and not pollute the instruction cache.
430+{-# NOINLINE snocWithRealloc #-}
431+snocWithRealloc :: forall m a. (MonadIO m, Storable a) =>
436+snocWithRealloc allocSize arr x = do
437+ let elemSize = sizeOf (undefined :: a)
438+ arr1 <- liftIO $ reallocWith "snocWith" allocSize elemSize arr
441+-- | @snocWith newSize arr elem@ mutates @arr@ to append @elem@, the length of
442+-- the array increases by 1.
444+-- If there is no reserved space available in @arr@ it is reallocated to a size
445+-- in bytes determined by the @newSize oldSize@ function, where @oldSize@ is
446+-- the original size of the array in bytes.
448+-- If the new array size is more than 'largeObjectThreshold' we automatically
449+-- round it up to 'blockSize'.
451+-- Note that the returned array may be a mutated version of the original array.
454+{-# INLINE snocWith #-}
455+snocWith :: forall m a. (MonadIO m, Storable a) =>
460+snocWith allocSize arr x = liftIO $ do
461+ let newEnd = aEnd arr `plusPtr` sizeOf (undefined :: a)
462+ if newEnd <= aBound arr
463+ then snocNewEnd newEnd arr x
464+ else snocWithRealloc allocSize arr x
466+-- | The array is mutated to append an additional element to it. If there
467+-- is no reserved space available in the array then it is reallocated to grow
468+-- it by 'arrayChunkSize' rounded up to 'blockSize' when the size becomes more
469+-- than 'largeObjectThreshold'.
471+-- Note that the returned array may be a mutated version of the original array.
473+-- >>> snocLinear = snocWith (+ arrayChunkSize)
475+-- Performs O(n^2) copies to grow but is thrifty on memory.
478+{-# INLINE snocLinear #-}
479+snocLinear :: forall m a. (MonadIO m, Storable a) => Array a -> a -> m (Array a)
480+snocLinear = snocWith (+ arrayChunkSize)
482+-- XXX round it to next power of 2.
484+-- | The array is mutated to append an additional element to it. If there is no
485+-- reserved space available in the array then it is reallocated to double the
488+-- This is useful to reduce allocations when appending unknown number of
491+-- Note that the returned array may be a mutated version of the original array.
493+-- >>> snoc = snocWith (* 2)
495+-- Performs O(n * log n) copies to grow, but is liberal with memory allocation.
360{-# INLINE snoc #-}
498{-# INLINE snoc #-}
361snoc :: forall m a. (MonadIO m, Storable a) => Array a -> a -> m (Array a)
499snoc :: forall m a. (MonadIO m, Storable a) => Array a -> a -> m (Array a)
362−snoc arr@Array {..} x =
500+snoc = snocWith (* 2)
365− let oldStart = unsafeForeignPtrToPtr aStart
366− size = aEnd `minusPtr` oldStart
367− newSize = size + sizeOf (undefined :: a)
369− Malloc.mallocForeignPtrAlignedBytes
371− (alignment (undefined :: a))
372− unsafeWithForeignPtrM newPtr $ \pNew -> liftIO $ do
373− memcpy (castPtr pNew) (castPtr oldStart) size
374− poke (pNew `plusPtr` size) x
375− touchForeignPtr aStart
379− , aEnd = pNew `plusPtr` (size + sizeOf (undefined :: a))
380− , aBound = pNew `plusPtr` newSize
384− touchForeignPtr aStart
385− return $ arr {aEnd = aEnd `plusPtr` sizeOf (undefined :: a)}
387-------------------------------------------------------------------------------
502-------------------------------------------------------------------------------
388-- Resizing
503-- Resizing