Add State monad benchmarks by Andras Kovacs
authorMatthew Pickering <matthewtpickering@gmail.com>
Tue, 15 Aug 2017 00:45:33 +0000 (20:45 -0400)
committerBen Gamari <ben@smart-cactus.org>
Tue, 15 Aug 2017 00:45:33 +0000 (20:45 -0400)
Summary:
They are originally from
https://github.com/AndrasKovacs/misc-stuff/blob/master/haskell/Eff/EffBench.hs

They show interesting interactions with call arity, spec constr and SAT.

Reviewers: O26 nofib, michalt, simonpj, bgamari

Reviewed By: bgamari

Subscribers: RyanGlScott

GHC Trac Issues: #13892

Differential Revision: https://phabricator.haskell.org/D3683

29 files changed:
real/Makefile
real/eff/CS/CS.stdout [new file with mode: 0644]
real/eff/CS/EffBench.hs [new file with mode: 0644]
real/eff/CS/Main.hs [new file with mode: 0644]
real/eff/CS/Makefile [new file with mode: 0644]
real/eff/CSD/CSD.stdout [new file with mode: 0644]
real/eff/CSD/EffBench.hs [new file with mode: 0644]
real/eff/CSD/Main.hs [new file with mode: 0644]
real/eff/CSD/Makefile [new file with mode: 0644]
real/eff/FS/EffBench.hs [new file with mode: 0644]
real/eff/FS/FS.stdout [new file with mode: 0644]
real/eff/FS/Main.hs [new file with mode: 0644]
real/eff/FS/Makefile [new file with mode: 0644]
real/eff/Makefile [new file with mode: 0644]
real/eff/S/Main.hs [new file with mode: 0644]
real/eff/S/Makefile [new file with mode: 0644]
real/eff/S/S.stdout [new file with mode: 0644]
real/eff/VS/EffBench.hs [new file with mode: 0644]
real/eff/VS/Main.hs [new file with mode: 0644]
real/eff/VS/Makefile [new file with mode: 0644]
real/eff/VS/VS.stdout [new file with mode: 0644]
real/eff/VSD/EffBench.hs [new file with mode: 0644]
real/eff/VSD/Main.hs [new file with mode: 0644]
real/eff/VSD/Makefile [new file with mode: 0644]
real/eff/VSD/VSD.stdout [new file with mode: 0644]
real/eff/VSM/EffBench.hs [new file with mode: 0644]
real/eff/VSM/Main.hs [new file with mode: 0644]
real/eff/VSM/Makefile [new file with mode: 0644]
real/eff/VSM/VSM.stdout [new file with mode: 0644]

index c9c4f5c..3f530a4 100644 (file)
@@ -3,7 +3,7 @@ include $(TOP)/mk/boilerplate.mk
 
 SUBDIRS = anna bspt cacheprof compress compress2 fem fluid fulsom gamteb gg \
           grep hidden hpg infer lift linear maillist mkhprog parser pic prolog \
-          reptile rsa scs symalg veritas
+          reptile rsa scs symalg veritas eff
 
 
 include $(TOP)/mk/target.mk
diff --git a/real/eff/CS/CS.stdout b/real/eff/CS/CS.stdout
new file mode 100644 (file)
index 0000000..3faedb7
--- /dev/null
@@ -0,0 +1 @@
+CS
diff --git a/real/eff/CS/EffBench.hs b/real/eff/CS/EffBench.hs
new file mode 100644 (file)
index 0000000..c0fc7c1
--- /dev/null
@@ -0,0 +1,71 @@
+{-# LANGUAGE RankNTypes, BangPatterns, ScopedTypeVariables,
+  MultiParamTypeClasses, FlexibleInstances, FlexibleContexts #-}
+
+{-# OPTIONS -fexpose-all-unfoldings #-}
+
+
+
+module EffBench where
+
+
+
+times :: Monad m => Int -> m a -> m ()
+times n ma = go n where
+  go 0 = pure ()
+  go n = ma >> go (n - 1)
+{-# inline times #-}
+
+
+-- inlined church free state
+--------------------------------------------------------------------------------
+-- IF, we run a late pass of SAT and modify the bound to run SAT on one
+-- static argument AND add an additional run of the simplifier then this
+-- optimises well. AND also need to run specConstr after doing SAT.
+
+newtype CS s a = CS {runCS ::
+     forall r.
+     (a -> r)          -- pure
+  -> ((s -> r) -> r)   -- get
+  -> (s -> r -> r)     -- put
+  -> r
+  }
+
+instance Functor (CS s) where
+
+  fmap f (CS g) = CS $ \pure get put -> g (pure . f) get put
+  {-# inline fmap #-}
+
+instance Applicative (CS s) where
+  pure a = CS $ \pure get put -> pure a
+  {-# inline pure #-}
+  CS mf <*> CS ma = CS $ \pure get put ->
+    mf (\f -> ma (pure . f) get put) get put
+  {-# inline (<*>) #-}
+
+instance Monad (CS s) where
+  return a = CS $ \pure get put -> pure a
+  {-# inline return #-}
+  CS ma >>= f = CS $ \pure get put ->
+    ma (\a -> runCS (f a) pure get put) get put
+  {-# inline (>>=) #-}
+  CS ma >> CS mb = CS $ \pure get put -> ma (\_ -> mb pure get put) get put
+  {-# inline (>>) #-}
+
+cmodify :: (s -> s) -> CS s ()
+cmodify f = CS $ \pure get put ->
+  get $ \s -> let !s' = f s in
+  put s' $
+  pure ()
+{-# inline cmodify #-}
+
+crunState :: CS s a -> s -> (a, s)
+crunState (CS f) = f
+  (\a s -> (a, s))
+  (\got s -> got s s)
+  (\s' put s -> put s')
+{-# inline crunState #-}
+
+test2 :: Int -> ((), Int)
+test2 n = crunState (times n (cmodify (+1))) 0
+
+
diff --git a/real/eff/CS/Main.hs b/real/eff/CS/Main.hs
new file mode 100644 (file)
index 0000000..82e79d5
--- /dev/null
@@ -0,0 +1,13 @@
+module Main (main) where
+
+import EffBench
+import Control.Exception.Base
+
+n :: Int
+n = 10000000
+
+main = do
+
+  putStrLn "CS"
+  evaluate $ crunState (times n $ cmodify (+1)) 0
+
diff --git a/real/eff/CS/Makefile b/real/eff/CS/Makefile
new file mode 100644 (file)
index 0000000..7b43fb5
--- /dev/null
@@ -0,0 +1,9 @@
+TOP = ../../..
+include $(TOP)/mk/boilerplate.mk
+
+SRC_RUNTEST_OPTS += -stdout-binary
+
+SRC_HC_OPTS += -fglasgow-exts -package transformers
+
+include $(TOP)/mk/target.mk
+
diff --git a/real/eff/CSD/CSD.stdout b/real/eff/CSD/CSD.stdout
new file mode 100644 (file)
index 0000000..abc2032
--- /dev/null
@@ -0,0 +1 @@
+CSD
diff --git a/real/eff/CSD/EffBench.hs b/real/eff/CSD/EffBench.hs
new file mode 100644 (file)
index 0000000..305bd98
--- /dev/null
@@ -0,0 +1,47 @@
+{-# LANGUAGE RankNTypes, BangPatterns, ScopedTypeVariables,
+  MultiParamTypeClasses, FlexibleInstances, FlexibleContexts #-}
+
+{-# OPTIONS -fexpose-all-unfoldings #-}
+
+
+
+module EffBench where
+
+import qualified Control.Monad.State.Strict as S
+
+
+times :: Monad m => Int -> m a -> m ()
+times n ma = go n where
+  go 0 = pure ()
+  go n = ma >> go (n - 1)
+{-# inline times #-}
+
+
+-- Classy chruch state
+--------------------------------------------------------------------------------
+--
+-- This optimises well as it's the role of the specialiser to specialise
+-- each `pure`, `get`, `put`.
+
+class PureD r a where cpure :: a -> r
+class GetD  s r where cget  :: (s -> r) -> r
+class PutD  s r where cput  :: s -> r -> r
+
+csmodify :: (GetD s r, PutD s r, PureD r ()) => (s -> s) -> r
+csmodify f = cget $ \s -> let !s' = f s in cput s' $ cpure ()
+
+cstimes :: forall r. (GetD Int r, PutD Int r, PureD r ()) => Int -> r
+cstimes 0 = cpure ()
+cstimes n = (cget :: (Int -> r) -> r) $ \s -> let !s' = s + 1 in (cput :: (Int -> r -> r)) s' $ cstimes (n - 1)
+
+instance PureD (s -> (a, s)) a where
+  cpure a s = (a, s)
+  {-# inline cpure #-}
+instance GetD  s (s -> (a, s)) where
+  cget got s = got s s
+  {-# inline cget #-}
+instance PutD s (s -> (a, s)) where
+  cput s' r _ = r s'
+  {-# inline cput #-}
+
+
diff --git a/real/eff/CSD/Main.hs b/real/eff/CSD/Main.hs
new file mode 100644 (file)
index 0000000..6f54157
--- /dev/null
@@ -0,0 +1,13 @@
+module Main (main) where
+
+import EffBench
+import Control.Exception.Base
+
+n :: Int
+n = 10000000
+
+main = do
+
+  putStrLn "CSD"
+  evaluate $ (cstimes :: Int -> (Int -> ((), Int))) n (0 :: Int)
+
diff --git a/real/eff/CSD/Makefile b/real/eff/CSD/Makefile
new file mode 100644 (file)
index 0000000..646e3d2
--- /dev/null
@@ -0,0 +1,9 @@
+TOP = ../../..
+include $(TOP)/mk/boilerplate.mk
+
+SRC_RUNTEST_OPTS += -stdout-binary
+
+SRC_HC_OPTS += -fglasgow-exts
+
+include $(TOP)/mk/target.mk
+
diff --git a/real/eff/FS/EffBench.hs b/real/eff/FS/EffBench.hs
new file mode 100644 (file)
index 0000000..a21a7a1
--- /dev/null
@@ -0,0 +1,57 @@
+{-# LANGUAGE RankNTypes, LambdaCase, ScopedTypeVariables,
+  MultiParamTypeClasses, FlexibleInstances, FlexibleContexts #-}
+
+{-# OPTIONS -fexpose-all-unfoldings #-}
+
+
+
+module EffBench where
+
+
+times :: Monad m => Int -> m a -> m ()
+times n ma = go n where
+  go 0 = pure ()
+  go n = ma >> go (n - 1)
+{-# inline times #-}
+
+-- inlined free state
+--------------------------------------------------------------------------------
+
+data FS s a = Pure a | Get (s -> FS s a) | Put !s (FS s a)
+
+instance Functor (FS s) where
+  fmap f = go where
+    go = \case
+      Pure a  -> Pure (f a)
+      Get k   -> Get (fmap f . k)
+      Put s k -> Put s (fmap f k)
+  {-#  inline fmap #-}
+
+instance Applicative (FS s) where
+  pure = Pure
+  Pure f  <*> ma = fmap f ma
+  Get k   <*> ma = Get ((<*> ma) . k)
+  Put s k <*> ma = Put s (k <*> ma)
+  {-# inline pure #-}
+  {-# inline (<*>) #-}
+
+instance Monad (FS s) where
+  return = Pure
+  Pure a  >>= f = f a
+  Get k   >>= f = Get ((>>= f) . k)
+  Put s k >>= f = Put s (k >>= f)
+  {-# inline return #-}
+  {-# inline (>>=) #-}
+
+fmodify :: (s -> s) -> FS s ()
+fmodify f =
+  Get $ \s ->
+  Put (f s) $
+  Pure ()
+{-# inline fmodify #-}
+
+frunState :: FS s a -> s -> (a, s)
+frunState (Pure a)   s = (a, s)
+frunState (Get k)    s = frunState (k s) s
+frunState (Put s' k) s = frunState k s'
+
diff --git a/real/eff/FS/FS.stdout b/real/eff/FS/FS.stdout
new file mode 100644 (file)
index 0000000..6a82ca7
--- /dev/null
@@ -0,0 +1 @@
+FS
diff --git a/real/eff/FS/Main.hs b/real/eff/FS/Main.hs
new file mode 100644 (file)
index 0000000..abea8ef
--- /dev/null
@@ -0,0 +1,13 @@
+module Main (main) where
+
+import EffBench
+import Control.Exception.Base
+
+n :: Int
+n = 10000000
+
+main = do
+
+  putStrLn "FS"
+  evaluate $ frunState (times n $ (fmodify (+1))) 0
+
diff --git a/real/eff/FS/Makefile b/real/eff/FS/Makefile
new file mode 100644 (file)
index 0000000..646e3d2
--- /dev/null
@@ -0,0 +1,9 @@
+TOP = ../../..
+include $(TOP)/mk/boilerplate.mk
+
+SRC_RUNTEST_OPTS += -stdout-binary
+
+SRC_HC_OPTS += -fglasgow-exts
+
+include $(TOP)/mk/target.mk
+
diff --git a/real/eff/Makefile b/real/eff/Makefile
new file mode 100644 (file)
index 0000000..1a0e8c3
--- /dev/null
@@ -0,0 +1,8 @@
+TOP = ../..
+include $(TOP)/mk/boilerplate.mk
+
+SUBDIRS = CS S FS VS VSM CSD VSD
+
+
+include $(TOP)/mk/target.mk
+
diff --git a/real/eff/S/Main.hs b/real/eff/S/Main.hs
new file mode 100644 (file)
index 0000000..08878b2
--- /dev/null
@@ -0,0 +1,19 @@
+module Main (main) where
+
+import Control.Exception.Base
+import qualified Control.Monad.State.Strict as S
+
+n :: Int
+n = 10000000
+
+times :: Monad m => Int -> m a -> m ()
+times n ma = go n where
+  go 0 = pure ()
+  go n = ma >> go (n - 1)
+{-# inline times #-}
+
+main = do
+
+  putStrLn "S"
+  evaluate $ S.runState (times n $ (S.modify (+1))) 0
+
diff --git a/real/eff/S/Makefile b/real/eff/S/Makefile
new file mode 100644 (file)
index 0000000..dc665af
--- /dev/null
@@ -0,0 +1,9 @@
+TOP = ../../..
+include $(TOP)/mk/boilerplate.mk
+
+SRC_RUNTEST_OPTS += -stdout-binary
+
+SRC_HC_OPTS += -fglasgow-exts -package transformers -package mtl -O2
+
+include $(TOP)/mk/target.mk
+
diff --git a/real/eff/S/S.stdout b/real/eff/S/S.stdout
new file mode 100644 (file)
index 0000000..3762249
--- /dev/null
@@ -0,0 +1 @@
+S
diff --git a/real/eff/VS/EffBench.hs b/real/eff/VS/EffBench.hs
new file mode 100644 (file)
index 0000000..49eabda
--- /dev/null
@@ -0,0 +1,70 @@
+{-# LANGUAGE RankNTypes, BangPatterns, ScopedTypeVariables,
+  MultiParamTypeClasses, FlexibleInstances, FlexibleContexts #-}
+
+{-# OPTIONS -fexpose-all-unfoldings #-}
+
+
+
+module EffBench where
+
+
+times :: Monad m => Int -> m a -> m ()
+times n ma = go n where
+  go 0 = pure ()
+  go n = ma >> go (n - 1)
+{-# inline times #-}
+
+
+
+-- inlined van Laarhoven free state
+--------------------------------------------------------------------------------
+
+newtype S s a = S {runS :: s -> (a, s)}
+
+newtype VS s a = VS { runVS ::
+     forall m.
+     (forall a. a -> m a)                   -- pure
+  -> (forall a b. m a -> (a -> m b) -> m b) -- bind
+  -> m s                                  -- get
+  -> (s -> m ())                            -- put
+  -> m a
+  }
+
+instance Functor (VS s) where
+  fmap f (VS g) = VS $ \pure (>>=) get put ->
+    g pure (>>=) get put >>= \a -> pure (f a)
+  {-# inline fmap #-}
+
+instance Applicative (VS s) where
+  pure a = VS $ \pure (>>=) get put -> pure a
+  VS mf <*> VS ma = VS $ \pure (>>=) get put ->
+    mf pure (>>=) get put >>= \f ->
+    ma pure (>>=) get put >>= \a -> pure (f a)
+  {-# inline pure #-}
+  {-# inline (<*>) #-}
+
+instance Monad (VS s) where
+  return a = VS $ \pure (>>=) get put -> pure a
+  VS ma >>= f = VS $ \pure (>>=) get put ->
+    ma pure (>>=) get put >>= \a -> runVS (f a) pure (>>=) get put
+  {-# inline return #-}
+
+vmodify :: (s -> s) -> VS s ()
+vmodify f = VS $ \pure (>>=) get put ->
+  get >>= \s ->
+  let !s' = f s in
+  put s'
+{-# inline vmodify #-}
+
+vrunState' :: VS s a -> S s a
+vrunState' (VS f) = f
+  (\a -> S $ \s -> (a, s))
+  (\(S ma) f -> S $ \s -> let !(!a, !s') = ma s; !(!b, !s'') = runS (f a) s' in (b, s''))
+  (S $ \s -> (s, s))
+  (\s' -> S $ const ((), s'))
+{-# inline vrunState' #-}
+
+vrunState :: VS s a -> s -> (a, s)
+vrunState x = runS (vrunState' x)
+{-# inline vrunState #-}
+
diff --git a/real/eff/VS/Main.hs b/real/eff/VS/Main.hs
new file mode 100644 (file)
index 0000000..029b9e1
--- /dev/null
@@ -0,0 +1,13 @@
+module Main (main) where
+
+import EffBench
+import Control.Exception.Base
+
+n :: Int
+n = 10000000
+
+main = do
+
+  putStrLn "VS"
+  evaluate $ vrunState (times n $ (vmodify (+1))) 0
+
diff --git a/real/eff/VS/Makefile b/real/eff/VS/Makefile
new file mode 100644 (file)
index 0000000..7b43fb5
--- /dev/null
@@ -0,0 +1,9 @@
+TOP = ../../..
+include $(TOP)/mk/boilerplate.mk
+
+SRC_RUNTEST_OPTS += -stdout-binary
+
+SRC_HC_OPTS += -fglasgow-exts -package transformers
+
+include $(TOP)/mk/target.mk
+
diff --git a/real/eff/VS/VS.stdout b/real/eff/VS/VS.stdout
new file mode 100644 (file)
index 0000000..4b23d5a
--- /dev/null
@@ -0,0 +1 @@
+VS
diff --git a/real/eff/VSD/EffBench.hs b/real/eff/VSD/EffBench.hs
new file mode 100644 (file)
index 0000000..6b77bbe
--- /dev/null
@@ -0,0 +1,35 @@
+{-# LANGUAGE RankNTypes, BangPatterns, ScopedTypeVariables,
+  MultiParamTypeClasses, FlexibleInstances, FlexibleContexts #-}
+
+{-# OPTIONS -fexpose-all-unfoldings #-}
+
+
+
+module EffBench where
+
+import qualified Control.Monad.State.Strict as S
+
+
+times :: Monad m => Int -> m a -> m ()
+times n ma = go n where
+  go 0 = pure ()
+  go n = ma >> go (n - 1)
+{-# inline times #-}
+
+-- classy van Laarhoven state (same as mtl)
+--------------------------------------------------------------------------------
+
+class VPutD s m where vput :: s -> m ()
+class VGetD s m where vget :: m s
+
+vmdmodify :: (Monad m, VPutD s m, VGetD s m) => (s -> s) -> m ()
+vmdmodify f = do
+  s <- vget
+  let !s' = f s
+  vput s'
+
+instance VPutD s (S.State s) where vput = S.put
+instance VGetD s (S.State s) where vget = S.get
+
+
+
diff --git a/real/eff/VSD/Main.hs b/real/eff/VSD/Main.hs
new file mode 100644 (file)
index 0000000..a24b66c
--- /dev/null
@@ -0,0 +1,14 @@
+module Main (main) where
+
+import EffBench
+import Control.Exception.Base
+import qualified Control.Monad.State.Strict as S
+
+n :: Int
+n = 10000000
+
+main = do
+
+  putStrLn "VSD"
+  evaluate $ S.runState (times n $ (vmdmodify :: (Int -> Int) -> S.State Int ())  (+1)) (0 :: Int)
+
diff --git a/real/eff/VSD/Makefile b/real/eff/VSD/Makefile
new file mode 100644 (file)
index 0000000..85f14b2
--- /dev/null
@@ -0,0 +1,9 @@
+TOP = ../../..
+include $(TOP)/mk/boilerplate.mk
+
+SRC_RUNTEST_OPTS += -stdout-binary
+
+SRC_HC_OPTS += -fglasgow-exts -package transformers -package mtl
+
+include $(TOP)/mk/target.mk
+
diff --git a/real/eff/VSD/VSD.stdout b/real/eff/VSD/VSD.stdout
new file mode 100644 (file)
index 0000000..87844bc
--- /dev/null
@@ -0,0 +1 @@
+VSD
diff --git a/real/eff/VSM/EffBench.hs b/real/eff/VSM/EffBench.hs
new file mode 100644 (file)
index 0000000..d1909da
--- /dev/null
@@ -0,0 +1,58 @@
+{-# LANGUAGE RankNTypes, BangPatterns, ScopedTypeVariables,
+  MultiParamTypeClasses, FlexibleInstances, FlexibleContexts #-}
+
+{-# OPTIONS -fexpose-all-unfoldings #-}
+
+
+
+module EffBench where
+
+import qualified Control.Monad.State.Strict as S
+
+
+times :: Monad m => Int -> m a -> m ()
+times n ma = go n where
+  go 0 = pure ()
+  go n = ma >> go (n - 1)
+{-# inline times #-}
+
+
+-- van Laarhoven state translated to mtl State
+--------------------------------------------------------------------------------
+
+newtype VSM s a = VSM { runVSM :: forall m. Monad m => m s -> (s -> m ()) -> m a}
+
+instance Functor (VSM s) where
+  fmap f (VSM g) = VSM $ \get put ->
+    g get put >>= \a -> pure (f a)
+  {-# inline fmap #-}
+
+instance Applicative (VSM s) where
+  pure a = VSM $ \get put -> pure a
+  VSM mf <*> VSM ma = VSM $ \get put ->
+    mf get put >>= \f ->
+    ma get put >>= \a -> pure (f a)
+  {-# inline pure #-}
+  {-# inline (<*>) #-}
+
+instance Monad (VSM s) where
+  return a = VSM $ \get put -> pure a
+  VSM ma >>= f = VSM $ \get put ->
+    ma get put >>= \a -> runVSM (f a) get put
+  {-# inline return #-}
+
+vmmodify :: (s -> s) -> VSM s ()
+vmmodify f = VSM $ \get put ->
+  get >>= \s ->
+  let !s' = f s in
+  put s'
+{-# inline vmmodify #-}
+
+vmrunState :: VSM s a -> s -> (a, s)
+vmrunState (VSM ma) = S.runState (ma S.get S.put)
+{-# inline vmrunState #-}
+
+test :: Int -> ((), Int)
+test n = vmrunState (times n $ vmmodify (+1)) n
+
+
diff --git a/real/eff/VSM/Main.hs b/real/eff/VSM/Main.hs
new file mode 100644 (file)
index 0000000..8a031e9
--- /dev/null
@@ -0,0 +1,13 @@
+module Main (main) where
+
+import EffBench
+import Control.Exception.Base
+
+n :: Int
+n = 10000000
+
+main = do
+
+  putStrLn "VSM"
+  evaluate $ vmrunState (times n $ vmmodify (+1)) 0
+
diff --git a/real/eff/VSM/Makefile b/real/eff/VSM/Makefile
new file mode 100644 (file)
index 0000000..85f14b2
--- /dev/null
@@ -0,0 +1,9 @@
+TOP = ../../..
+include $(TOP)/mk/boilerplate.mk
+
+SRC_RUNTEST_OPTS += -stdout-binary
+
+SRC_HC_OPTS += -fglasgow-exts -package transformers -package mtl
+
+include $(TOP)/mk/target.mk
+
diff --git a/real/eff/VSM/VSM.stdout b/real/eff/VSM/VSM.stdout
new file mode 100644 (file)
index 0000000..bf493b3
--- /dev/null
@@ -0,0 +1 @@
+VSM