-
Notifications
You must be signed in to change notification settings - Fork 30
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Memory explodes trying to consume chunked pure stream #115
Comments
Can you include a minimal reproduction? |
Stream type: data Stream f m r
= Step !(f (Stream f m r))
| Effect (m (Stream f m r))
| Return r
data Of a b = !a :> b So, Step (a :> rest) When we reach a Note that none of the functions you listed actually force |
Here is something where I tried to replicate your issue: ❯ cat StreamingChunks.hs
{-# language ImportQualifiedPost #-}
module Main (main) where
import Data.Bifunctor (first)
import Data.ByteString (ByteString)
import Data.ByteString qualified as B
import Data.ByteString.Char8 qualified as BC8
import Data.List qualified as List
import Data.Word (Word8)
import Streaming (Stream)
import Streaming qualified as S
import Streaming.Prelude (Of)
import Streaming.Prelude qualified as S
import System.IO qualified as IO
main :: IO ()
main = do
S.mapM_ BC8.putStr
$ packBytes
$ S.each (List.replicate streamSize asciiA)
packBytes :: (Monad m) => Stream (Of Word8) m r -> Stream (Of ByteString) m r
packBytes s = id
$ S.mapsM (fmap (S.mapOf B.pack) . S.toList)
$ S.chunksOf chunkSize
$ s
asciiA :: Word8
asciiA = 65
streamSize :: Int
streamSize = 10_000_000
chunkSize :: Int
chunkSize = 32 The runtime stats:
~3.7 GB total allocations, but only 36K max residency. So, my guess is that whatever you were doing must be more complicated than this. |
I was trying to use
mapsM
andchunksOf
to go fromStream (Of Word8)
toStream (Of ByteString)
, and found that consuming even just 200MB was allocating dozens of Gigabytes memory. Discovered that the problem was eagerness. My infinite source(Of Word8)
was a series ofStep
s, which are strict on theFunctor
(why?). If I userepeats
to construct theStream
, then the code behaves sanely again, albeit taking twice as long as usingsplitsAt
by hand since, to add laziness,repeats
interleaves pureEffect
s into the stream. (WhichS.each
does not do.)Things to consider:
Step
(unless there's a good reason for it to have a bang).Step
constructorS.each
(by addingEffect
s to it likerepeats
, or better using another constructor)The text was updated successfully, but these errors were encountered: