1{-# LANGUAGE TemplateHaskell #-}
5{-# OPTIONS_GHC -ddump-simpl -ddump-to-file -dsuppress-all #-}
9-- Module : Streamly.Benchmark.Data.Serialize
10-- Copyright : (c) 2023 Composewell
11-- License : BSD-3-Clause
12-- Maintainer : streamly@composewell.com
14module Main (main) where
16-------------------------------------------------------------------------------
18-------------------------------------------------------------------------------
20import Control.DeepSeq (NFData(..))
21import Control.Exception (assert)
22import Control.Monad (replicateM_)
23import GHC.Generics (Generic)
24import System.Random (randomRIO)
26import Test.QuickCheck (Arbitrary, arbitrary)
30import Data.Proxy (Proxy(..))
31import Streamly.Internal.Data.Unbox
33import Control.DeepSeq (force)
34import Test.QuickCheck (oneof, generate)
35import Streamly.Internal.Data.Unbox (newBytes)
36import Streamly.Internal.Data.Serialize
41import Streamly.Internal.Data.Unbox.TH
43import Streamly.Internal.Data.Serialize.TH
48import Streamly.Benchmark.Common
51#define SERIALIZE_CLASS Unbox
52#define DERIVE_CLASS deriveUnbox
53#define SERIALIZE_OP pokeByteIndex
54#define DESERIALIZE_OP peekByteIndex
56#define SERIALIZE_CLASS Serialize
57#define DERIVE_CLASS deriveSerialize
58#define SERIALIZE_OP serialize
59#define DESERIALIZE_OP deserialize
62-------------------------------------------------------------------------------
64-------------------------------------------------------------------------------
67 deriving (Generic, Show, Eq)
70instance SERIALIZE_CLASS Unit
78 deriving (Generic, Show, Eq)
81instance SERIALIZE_CLASS Sum2
112 deriving (Generic, Show, Eq)
115instance SERIALIZE_CLASS Sum25
117$(DERIVE_CLASS ''Sum25)
147 deriving (Generic, Show)
150instance SERIALIZE_CLASS Product25
152$(DERIVE_CLASS ''Product25)
155-- XXX derived Eq instance is not inlined
156instance Eq Product25 where
159 (Product25 a1 a2 a3 a4 a5 a6 a7 a8 a9 a10 a11 a12 a13 a14 a15 a16 a17 a18 a19 a20 a21 a22 a23 a24 a25)
160 (Product25 b1 b2 b3 b4 b5 b6 b7 b8 b9 b10 b11 b12 b13 b14 b15 b16 b17 b18 b19 b20 b21 b22 b23 b24 b25)
187-------------------------------------------------------------------------------
189-- Simple non-recursive ADT
194 deriving (Generic, Show, Eq)
197instance SERIALIZE_CLASS CustomDT1
199$(DERIVE_CLASS ''CustomDT1)
202-------------------------------------------------------------------------------
204{-# INLINE getSize #-}
205getSize :: forall a. SERIALIZE_CLASS a => a -> Int
207getSize _ = sizeOf (Proxy :: Proxy a)
210 case size :: Size a of
216{-# INLINE sizeOfOnce #-}
217sizeOfOnce :: forall a. Serialize a => a -> Int
218sizeOfOnce val = getSize val
222 = Tree (BinTree a) (BinTree a)
224 deriving (Show, Read, Eq, Generic)
226-- XXX If a is ConstSize we can use an array to represent the binary tree. If
227-- the tree is balanced space wastage would be minimum.
229instance Serialize (BinTree a)
231$(deriveSerialize ''BinTree)
234instance NFData a => NFData (BinTree a) where
235 rnf (Leaf a) = rnf a `seq` ()
236 rnf (Tree l r) = rnf l `seq` rnf r `seq` ()
238instance Arbitrary a => Arbitrary (BinTree a) where
239 arbitrary = oneof [Leaf <$> arbitrary, Tree <$> arbitrary <*> arbitrary]
241mkBinTree :: (Arbitrary a) => Int -> IO (BinTree a)
242mkBinTree = go (generate $ arbitrary)
247 go r n = Tree <$> go r (n - 1) <*> go r (n - 1)
251prop = forAll arbitrary (ioProperty . test)
255 test :: BinTree Int -> IO Bool
258 case size :: Size (BinTree Int) of
262 _ <- serialize 0 arr t
263 (_, t1) <- deserialize 0 arr
267runQC = quickCheckWith stdArgs {maxSuccess = 100} prop
271-------------------------------------------------------------------------------
273-------------------------------------------------------------------------------
275{-# INLINE pokeTimes #-}
276pokeTimes :: forall a. SERIALIZE_CLASS a => a -> Int -> IO ()
277pokeTimes val times = do
278 -- XXX Size computation should also be part of the loop measuring
279 -- serialization cost.
282 replicateM_ times $ do
283 SERIALIZE_OP 0 arr val
285{-# INLINE peekTimes #-}
286peekTimes :: forall a. (Eq a, SERIALIZE_CLASS a) => Int -> a -> Int -> IO ()
287peekTimes n val times = do
289 _ <- SERIALIZE_OP 0 arr val
290 replicateM_ times $ do
296 <- DESERIALIZE_OP 0 arr
297 -- Ensure that we are actually constructing the type and using it.
298 -- Otherwise we may just read the values and discard them.
299 -- The comparison adds to the cost though.
302 then error "peek: no match"
305{-# INLINE roundtrip #-}
306roundtrip :: forall a. (Eq a, SERIALIZE_CLASS a) => a -> Int -> IO ()
307roundtrip val times = do
308 -- XXX Size computation should also be part of the loop measuring
309 -- serialization cost.
312 replicateM_ times $ do
313 _ <- SERIALIZE_OP 0 arr val
319 <- DESERIALIZE_OP 0 arr
320 assert (val == val1) (pure ())
321 -- So that the compiler does not optimize it out
323 then error "roundtrip: no match"
326benchSink :: NFData b => String -> Int -> (Int -> IO b) -> Benchmark
327benchSink name times f = bench name (nfIO (randomRIO (times, times) >>= f))
329-------------------------------------------------------------------------------
331-------------------------------------------------------------------------------
333-- Times is scaled by the number of constructors to normalize
335allBenchmarks :: Int -> [Benchmark]
338allBenchmarks :: BinTree Int -> [Int] -> Int -> [Benchmark]
339allBenchmarks tInt lInt times =
344 bench "bintree-int" $ nf sizeOfOnce tInt
345 , bench "list-int" $ nf sizeOfOnce lInt
349 [ benchSink "C1" times
351 , benchSink "C2" (times `div` 2)
352 (pokeTimes (CDT1C2 5))
353 , benchSink "C3" (times `div` 3)
354 (pokeTimes (CDT1C3 5 2))
355 , benchSink "Sum2" times
357 , benchSink "Sum25" times
359 , benchSink "Product25" (times `div` 26)
360 (pokeTimes (Product25 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25))
362 , benchSink "bintree-int" 1
364 , benchSink "list-int" 1
369 [ let !n = getSize Unit
370 in benchSink "Unit" times (peekTimes n Unit)
371 , let !n = getSize CDT1C1
372 in benchSink "C1" times (peekTimes n CDT1C1)
375 in benchSink "C2" (times `div` 2) (peekTimes n val)
376 , let val = CDT1C3 5 2
378 in benchSink "C3" (times `div` 3) (peekTimes n val)
379 , let !n = getSize Sum21
380 in benchSink "Sum2" times (peekTimes n Sum21)
381 , let !n = getSize Sum2525
382 in benchSink "Sum25" times (peekTimes n Sum2525)
383 , let val = Product25 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
385 in benchSink "Product25" (times `div` 26) (peekTimes n val)
387 , let !n = getSize tInt
388 in benchSink "bintree-int" 1 (peekTimes n tInt)
389 , let !n = getSize lInt
390 in benchSink "list-int" 1 (peekTimes n lInt)
394 [ benchSink "C1" times
396 , benchSink "C2" (times `div` 2)
397 (roundtrip (CDT1C2 5))
398 , benchSink "C3" (times `div` 3)
399 (roundtrip (CDT1C3 5 2))
400 , benchSink "Sum2" times
402 , benchSink "Sum25" times
404 , benchSink "Product25" (times `div` 26)
405 (roundtrip (Product25 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25))
407 , benchSink "bintree-int" 1
409 , benchSink "list-int" 1
415-------------------------------------------------------------------------------
417-------------------------------------------------------------------------------
422 -- Approximately 100000 constructors
423 -- Assuming Leaf nodes constitute two constructors (Leaf, Int) and internal
424 -- nodes 1 level = log_2 (100001/3) + 1 = 16
425 !tInt <- force <$> mkBinTree 16
427 -- Approximately 100000 constructors, assuming two constructors (Cons, Int)
429 let lInt = [1..50000 :: Int]
430 let !len = length lInt -- evaluate the list
433 -- This can take too much memory/CPU, need to restrict the test
436 runWithCLIOpts defaultStreamSize allBenchmarks
438 len `seq` runWithCLIOpts defaultStreamSize (allBenchmarks tInt lInt)
441 -- Enable FUSION_CHECK macro at the beginning of the file
442 -- Enable one benchmark below, and run the benchmark
443 -- Check the .dump-simpl output
445 -- peekTimes ((CDT1C2 (5 :: Int)) :: CustomDT1) value
446 -- peekTimes (Sum2525) value
447 -- peekTimes (Product25 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25) value
448 -- !(tInt :: BinTree Int) <- force <$> mkBinTree 16
449 -- print $ sizeOfOnce tInt
451 -- roundtrip ((CDT1C2 (5 :: Int)) :: CustomDT1) value
452 let !n = getSize lEnum
453 in peekTimes n lEnum 1
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list