Frames-streamly-0.1.0.3: examples/Strictness.hs
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeOperators #-}
--{-# OPTIONS_GHC -O0 #-}
module Main where
import StrictnessPaths
import qualified Data.Text as T
import qualified Data.Serialize as S
import qualified Data.ByteString as BS
import qualified Data.ByteString.Lazy as BL
import qualified Data.ByteString.Builder as BB
import qualified ByteString.StrictBuilder as BSB
import qualified Control.Foldl as FL
import qualified Control.Monad.State as ST
import qualified Data.Word as Word
import qualified Frames as F
import qualified Frames.Streamly.CSV as FStreamly
import qualified Frames.Streamly.InCore as FStreamly
import qualified Serialize as FS
import qualified Frames.CSV as Frames
import qualified Streamly.Data.Fold as Streamly.Fold
import qualified Streamly.Internal.Data.Fold.Types as Streamly.Fold
import qualified Streamly.Internal.Data.Fold as Streamly.Fold
import qualified Streamly.Prelude as Streamly
import qualified Streamly.Internal.Prelude as Streamly
import qualified Streamly as Streamly
import qualified Streamly.Internal.FileSystem.File
as Streamly.File
import qualified Streamly.External.ByteString as Streamly.ByteString
import qualified Streamly.Internal.Data.Array as Streamly.Data.Array
import qualified Streamly.Internal.Memory.Array as Streamly.Memory.Array
import qualified System.Clock
import qualified Data.Vector as V
import qualified Data.Vector.Mutable as VM
import qualified Control.Monad.Primitive as Prim
import qualified Data.Strict.Either as Strict
import qualified Data.Strict.Maybe as Strict
F.tableTypes' pumsACS1YrRowGen
main :: IO ()
main= do
testInIO
encodeOne :: S.Serialize a => a -> BB.Builder
encodeOne !x = S.execPut $ S.put x
bldrToCT = Streamly.ByteString.toArray . BL.toStrict . BB.toLazyByteString
encodeBSB :: S.Serialize a => a -> BSB.Builder
encodeBSB !x = BSB.bytes $! encodeBS x
encodeBS :: S.Serialize a => a -> BS.ByteString
encodeBS !x = S.runPut $! S.put x
bsbToCT = Streamly.ByteString.toArray . BSB.builderBytes
data Accum b = Accum { count :: !Int, bldr :: !b }
streamlySerializeF :: forall c bldr m a ct.(Monad m, Monoid bldr, c a, c Word.Word64)
=> (forall b. c b => b -> bldr)
-> (bldr -> ct)
-> Streamly.Fold.Fold m a ct
streamlySerializeF encodeOne bldrToCT = Streamly.Fold.Fold step initial extract where
step (Accum n b) !a = return $ Accum (n + 1) (b <> encodeOne a)
initial = return $ Accum 0 mempty
extract (Accum n b) = return $ bldrToCT $ encodeOne (fromIntegral @Int @Word.Word64 n) <> b
{-# INLINEABLE streamlySerializeF #-}
toCT :: BSB.Builder -> Int -> Streamly.Memory.Array.Array Word8
toCT bldr n = bsbToCT $ encodeBSB (fromIntegral @Int @Word.Word64 n) <> bldr
streamlySerializeF2 :: forall c bldr m a ct.(Monad m, Monoid bldr, c a, c Word.Word64)
=> (forall b. c b => b -> bldr)
-> (bldr -> ct)
-> Streamly.Fold.Fold m a ct
streamlySerializeF2 encodeOne bldrToCT =
let fBuilder = Streamly.Fold.Fold step initial return where
step !b !a = return $ b <> encodeOne a
initial = return mempty
toCT' bldr n = bldrToCT $ encodeOne (fromIntegral @Int @Word.Word64 n) <> bldr
in toCT' <$> fBuilder <*> Streamly.Fold.length
-- extract (Accum n b) = return $ bldrToCT $ encodeOne (fromIntegral @Int @Word.Word64 n) <> b
{-# INLINEABLE streamlySerializeF2 #-}
type SmallRow = [PUMSYEAR, PUMSPERWT, PUMSSTATEFIP, PUMSREGION, PUMSPUMA, PUMSMETRO, PUMSDENSITY, PUMSAGE]
testInIO :: IO ()
testInIO = do
let pumsCSV = "example_data/acs100k.csv"
putTextLn "Testing File.toBytes..."
let rawBytesS = Streamly.File.toBytes pumsCSV
rawBytes <- Streamly.fold Streamly.Fold.length rawBytesS
putTextLn $ "raw PUMS data has " <> show rawBytes <> " bytes."
putTextLn "Testing readTable..."
let sPUMSRawRows :: Streamly.SerialT IO PUMS_Raw
= FStreamly.readTableOpt Frames.defaultParser pumsCSV
iRows <- Streamly.fold Streamly.Fold.length sPUMSRawRows
putTextLn $ "raw PUMS data has " <> (T.pack $ show iRows) <> " rows."
putTextLn "Testing Frames.Streamly.inCoreAoS:"
fPums <- FStreamly.inCoreAoS sPUMSRawRows
putTextLn $ "raw PUMS frame has " <> show (FL.fold FL.length fPums) <> " rows."
putTextLn "frame to vec"
vPostFrame :: V.Vector PUMS_Raw <- FL.foldM FL.vectorM fPums
putTextLn $ "vPostFrame has " <> show (V.length vPostFrame) <> " rows"
putTextLn "frame to array"
aPostFrame :: Streamly.Data.Array.Array PUMS_Raw <- Streamly.fold Streamly.Data.Array.write $ Streamly.fromFoldable fPums
putTextLn $ "aPostFrame has " <> show (Streamly.Data.Array.length aPostFrame) <> " rows"
{-
-- Previous goes up to 28MB, looks like via doubling. Then to 0 (collects fPums after counting?)
-- This one then climbs to 10MB, rows are smaller. No large leaks.
let f :: PUMS_Raw -> F.Record SmallRow
f !x = F.rcast x
putTextLn "Testing Frames.Streamly.inCoreAoS with row transform:"
fPums' :: F.FrameRec SmallRow <- FStreamly.inCoreAoS $ Streamly.map f sPUMSRawRows
putTextLn $ "transformed PUMS frame has " <> show (FL.fold FL.length fPums') <> " rows."
putTextLn "v1"
-- sDict = KS.cerealStreamlyDict
let countFold = runningCountF "reading..." (\n -> "read " <> show (250000 * n) <> " rows") "finished"
sPUMSRunningCount = Streamly.map f
$ Streamly.tapOffsetEvery 250000 250000 countFold sPUMSRawRows
sPUMSRCToS = Streamly.map FS.toS sPUMSRunningCount
serializedBytes :: Streamly.Memory.Array.Array Word.Word8 <- Streamly.fold (streamlySerializeF2 @S.Serialize encodeBSB bsbToCT) sPUMSRCToS
print $ Streamly.Memory.Array.length serializedBytes
putTextLn "v4"
bldr <- Streamly.foldl' (\acc !x -> let b = S.runPut (S.put x) in b `seq` (acc <> BSB.bytes b)) mempty sPUMSRCToS
print $ BS.length $ BSB.builderBytes bldr
putTextLn "In stages"
putTextLn "Word8: "
w8Array <- Streamly.Data.Array.fromStream $ Streamly.File.toBytes pumsCSV
putTextLn $ "w8 array is " <> show (Streamly.Data.Array.length w8Array) <> " long."
putTextLn "To Vector"
putTextLn "Word8: "
vWord8 <- streamToVector $ FStreamly.streamWord8 pumsCSV
putTextLn $ "w8 vector is " <> show (V.length vWord8) <> " long."
putTextLn "text lines (via vector)"
vTextLine <- streamToVector $ FStreamly.streamTextLines pumsCSV
putTextLn $ "vTextLine has " <> show (V.length vTextLine) <> " elements."
putTextLn "text lines (via mutable vector)"
mvTextLine <- streamToVector2 100 $ FStreamly.streamTextLines pumsCSV
putTextLn $ "mvTextLine has " <> show (V.length mvTextLine) <> " elements."
putTextLn "tokenized (via vector)"
vTokenized <- streamToVector $ FStreamly.streamTokenized pumsCSV
putTextLn $ "vTokenized has " <> show (V.length vTokenized) <> " elements."
putTextLn "parsed (via vector)"
vParsed :: V.Vector (F.Rec (Strict.Either Text F.:. F.ElField) (F.RecordColumns PUMS_Raw)) <- streamToVector $ FStreamly.streamParsed pumsCSV
putTextLn $ "vParsed has " <> show (V.length vParsed) <> " elements."
putTextLn "parsedMaybe (via vector)"
vParsedMaybe :: V.Vector (F.Rec (Maybe F.:. F.ElField) (F.RecordColumns PUMS_Raw)) <- streamToVector $ FStreamly.streamParsedMaybe pumsCSV
putTextLn $ "vParsedMaybe has " <> show (V.length vParsedMaybe) <> " elements."
-}
{-
putTextLn "Copy to boxed array"
array <- Streamly.Data.Array.fromStream $ sPUMSRunningCount
putTextLn $ "array has " <> show (Streamly.Data.Array.length array) <> " elements."
-}
streamToVector :: Monad m => Streamly.SerialT m a -> m (V.Vector a)
streamToVector = V.unfoldrM strictUncons
strictUncons :: Monad m => Streamly.SerialT m a -> m (Maybe (a, Streamly.SerialT m a))
strictUncons s = do
lu <- Streamly.uncons s
case lu of
Nothing -> return Nothing
Just (!a, s') -> return $ Just (a, s')
{-# INLINE strictUncons #-}
streamToVector2 :: (Monad m, Prim.PrimMonad m) => Int -> Streamly.SerialT m a -> m (V.Vector a)
streamToVector2 initialSize s0 = do
let go curLength curN s v = do
mNext <- Streamly.uncons s
case mNext of
Nothing -> V.unsafeFreeze (VM.unsafeTake curN v)
Just (!a, s') -> do
(curLength', v') <- if curN < curLength
then return (curLength, v)
else VM.grow v (2 * curLength) >>= \v' -> return (2 * curLength, v')
VM.write v' curN a
go curLength' (curN + 1) s' v'
v0 <- VM.new initialSize
go initialSize 0 s0 v0
runningCountF :: ST.MonadIO m => T.Text -> (Int -> T.Text) -> T.Text -> Streamly.Fold.Fold m a ()
runningCountF startMsg countMsg endMsg = Streamly.Fold.Fold step start done where
start = ST.liftIO (putText startMsg) >> return 0
step !n _ = ST.liftIO $ do
t <- System.Clock.getTime System.Clock.ProcessCPUTime
putStr $ show t ++ ": "
putTextLn $ countMsg n
return (n+1)
done _ = ST.liftIO $ putTextLn endMsg