Skip to content
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

WIP: Refactor Requester modules #393

Closed
wants to merge 27 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
16c831a
WIP
Sep 15, 2019
8bad293
More WIP (doesn't build)
Sep 17, 2019
cc30af8
Still WIP, but seems to be working
Sep 19, 2019
7de5d29
Eliminate some more traces
Sep 20, 2019
8ab973d
Try using NonEmptyDeferred
Sep 20, 2019
e690a4c
Restrict Data.List.NonEmpty.Deferred's interface
Sep 20, 2019
da443ce
Add Data.List.NonEmpty.Deferred to cabal file
Sep 20, 2019
7b1bb4c
Use NonEmptyDeferred and direct sconcat (which uses mergeInt under th…
Sep 20, 2019
5394dbf
Various simplifications
Sep 20, 2019
476ef2d
Some more random cleanups
Sep 20, 2019
58e6fa6
Switch to using fake phantom type
Sep 20, 2019
dfe6dbd
A bit of messing around that seems to help performance
ryantrinkle Sep 27, 2019
168e497
Improve perf of coincidencePatch*
ryantrinkle Sep 29, 2019
c3fc99f
Misc cleanups
ryantrinkle Sep 29, 2019
64f3ce6
restore forRequesterData
JBetz Jan 7, 2020
d9b52ab
reenable RequesterT tests
JBetz Jan 8, 2020
4a0200f
add test case for broken Adjustable networks (#369)
JBetz Jan 8, 2020
b9693ac
add bang pattern to expected test result
JBetz Jan 8, 2020
3697e45
WIP: resurrecting functions we still want
JBetz Jan 14, 2020
6fc39ba
fix decoder definition and mixed up existentials
JBetz Jan 15, 2020
3aa48a1
restore some helper functions
JBetz Jan 16, 2020
81f1d26
rename requestEnvelopesToList to requestEnvelopesToDSums
JBetz Jan 16, 2020
93ab83d
add tests for matchResponsesWithRequests
JBetz Jan 16, 2020
d0a3a85
test case for performEvent
JBetz Jan 16, 2020
c2815df
update changelog for requester refactor
JBetz Jan 17, 2020
2aec0ec
Fix rebase issues
Feb 12, 2020
03123b6
[WIP] Split holdDynChain benchmark out to remove Criterion
Feb 11, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,25 @@
* Add cabal flags `debug-propagation` and `debug-event-cycles` to build in debugging
code for performance and for cyclic dependencies between events

* Refactor of `Reflex.Requester`:
* Updated:
* `RequesterData` to `RequestData`, `RequesterEnvelope`, and `ResponseData`
* `singletonRequesterData` to `singletonRequestData` and `singletonResponseData`
* `requesterDataToList` to `requestEnvelopesToDSums`
* Removed:
* `RequesterDataKey`
* `multiEntry`
* `unMultiEntry`
* `withRequesterT`
* `runWithReplaceRequesterTWith`
* `requesting'`
* `traverseIntMapWithKeyWithAdjustRequesterTWith`
* `traverseDMapWithKeyWithAdjustRequesterTWith`

* Added `Data.List.Deferred` and `Data.List.NonEmpty.Deferred` for optimizing `<>` operations.

* Added `Data.TagMap`, `Reflex.FanTag`, and `Data.Unique.Tag.Local` to improve request and response tagging.

## 0.6.3

* `Data.WeakBag.traverse` and `Data.FastWeakBag.traverse` have been deprecated.
Expand Down
142 changes: 142 additions & 0 deletions bench/HoldDynChain.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE ForeignFunctionInterface #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE TypeSynonymInstances #-}
{-# LANGUAGE ViewPatterns #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}

module Main where

import Criterion.Main
import Criterion.Types

import Reflex
import Reflex.Host.Class

import Reflex.Plan.Reflex
import Reflex.TestPlan

import qualified Reflex.Bench.Focused as Focused
import Reflex.Spider.Internal (SpiderEventHandle)

import Control.Applicative
import Control.DeepSeq (NFData (..))

import Prelude
import System.IO
import System.Mem

import Control.Arrow
import Control.Concurrent
import Control.Concurrent.STM
import Control.Exception
import Control.Monad
import Control.Monad.Trans
import Data.Bool
import Data.Function
import Data.Int
import Data.IORef
import Data.Monoid
import Data.Time.Clock
import Debug.Trace.LocationTH
import GHC.Stats
import System.Environment
import System.Mem.Weak
import System.Process
import Text.Read

import Unsafe.Coerce

import Data.Map (Map)
import qualified Data.Map as Map


type MonadReflexHost' t m = (MonadReflexHost t m, MonadIORef m, MonadIORef (HostFrame t))


setupFiring :: (MonadReflexHost t m, MonadIORef m) => Plan t (Event t a) -> m (EventHandle t a, Schedule t)
setupFiring p = do
(e, s) <- runPlan p
h <- subscribeEvent e
return (h, s)

-- Hack to avoid the NFData constraint for EventHandle which is a synonym
newtype Ignore a = Ignore a
instance NFData (Ignore a) where
rnf !_ = ()

instance NFData (SpiderEventHandle x a) where
rnf !_ = ()

instance NFData (Behavior t a) where
rnf !_ = ()

instance NFData (Firing t) where
rnf !_ = ()

-- Measure the running time
benchFiring :: forall t m. (MonadReflexHost' t m, MonadSample t m) => (forall a. m a -> IO a) -> TestCase -> Int -> IO ()
benchFiring runHost tc n = runHost $ do
let runIterations :: m a -> m ()
runIterations test = replicateM_ (10*n) $ do
result <- test
liftIO $ evaluate result
case tc of
TestE p -> do
(h, s) <- setupFiring p
runIterations $ readSchedule_ s $ readEvent' h
TestB p -> do
(b, s) <- runPlan p
runIterations $ readSchedule_ (makeDense s) $ sample b

benchmarks :: [(String, Int -> IO ())]
benchmarks = implGroup "spider" runSpiderHost cases
where
implGroup :: (MonadReflexHost' t m, MonadSample t m) => String -> (forall a. m a -> IO a) -> [(String, TestCase)] -> [(String, Int -> IO ())]
implGroup name runHost = group name . fmap (second (benchFiring runHost))
group name = fmap $ first ((name <> "/") <>)
dynamics n = group ("dynamics " <> show n) $ dynamics' n
dynamics' :: Word -> [(String, TestCase)]
dynamics' n = [ testE "holdDynChain" $ fmap updated $ holdDynChain n =<< d ]
d :: TestPlan t m => m (Dynamic t Word)
d = count =<< Focused.events 10
cases = concat
[ dynamics 100
, dynamics 1000
]

holdDynChain :: (Reflex t, MonadHold t m) => Word -> Dynamic t Word -> m (Dynamic t Word)
holdDynChain = Focused.iterM (\d' -> sample (current d') >>= flip holdDyn (updated d'))

pattern RunTestCaseFlag = "--run-test-case"

spawnBenchmark :: String -> Benchmark
spawnBenchmark name = bench name . toBenchmarkable $ \n -> do
self <- getExecutablePath
callProcess self [RunTestCaseFlag, name, show n, "+RTS", "-N1"]

foreign import ccall unsafe "myCapabilityHasOtherRunnableThreads" myCapabilityHasOtherRunnableThreads :: IO Bool

main :: IO ()
main = do
args <- getArgs
case args of
RunTestCaseFlag : t -> case t of
[name, readMaybe -> Just count] -> do
case lookup name benchmarks of
Just testCase -> testCase count
performGC
fix $ \loop -> bool (return ()) (yield >> loop) =<< myCapabilityHasOtherRunnableThreads
return ()
_ -> error "--run-test-case: expected test name and iteration count to follow"
_ -> defaultMainWith (defaultConfig { timeLimit = 20, csvFile = Just "dmap-original.csv", reportFile = Just "report.html" }) $ fmap (spawnBenchmark . fst) benchmarks
70 changes: 65 additions & 5 deletions reflex.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ library
containers >= 0.6 && < 0.7,
data-default >= 0.5 && < 0.8,
dependent-map >= 0.3 && < 0.4,
dlist == 0.8.*,
exception-transformers == 0.4.*,
lens >= 4.7 && < 5,
monad-control >= 1.0.1 && < 1.1,
Expand Down Expand Up @@ -100,13 +101,19 @@ library
Data.AppendMap,
Data.FastMutableIntMap,
Data.FastWeakBag,
Data.List.Deferred,
Data.List.NonEmpty.Deferred,
Data.List.NonEmpty.Deferred.Internal,
Data.Map.Misc,
Data.TagMap,
Data.Unique.Tag.Local,
Data.Unique.Tag.Local.Internal,
Data.WeakBag,
Reflex,
Reflex.Class,
Reflex.Adjustable.Class,
Reflex.BehaviorWriter.Base,
Reflex.BehaviorWriter.Class,
Reflex.Class,
Reflex.Collection,
Reflex.Dynamic,
Reflex.Dynamic.Uniq,
Expand All @@ -116,6 +123,7 @@ library
Reflex.EventWriter,
Reflex.EventWriter.Base,
Reflex.EventWriter.Class,
Reflex.FanTag,
Reflex.FastWeak,
Reflex.FunctorMaybe,
Reflex.Host.Class,
Expand All @@ -130,6 +138,7 @@ library
Reflex.Query.Base,
Reflex.Query.Class,
Reflex.Requester.Base,
Reflex.Requester.Base.Internal,
Reflex.Requester.Class,
Reflex.Spider,
Reflex.Spider.Internal,
Expand All @@ -150,6 +159,7 @@ library
patch:Data.Patch.MapWithMove as Reflex.Patch.MapWithMove

ghc-options: -Wall -fwarn-redundant-constraints -fwarn-tabs -funbox-strict-fields -O2 -fspecialise-aggressively
ghc-prof-options: -fprof-auto-calls

if flag(debug-trace-events)
cpp-options: -DDEBUG_TRACE_EVENTS
Expand Down Expand Up @@ -192,6 +202,7 @@ test-suite semantics
main-is: semantics.hs
hs-source-dirs: test
ghc-options: -O2 -Wall -rtsopts
ghc-prof-options: -fprof-auto-calls
build-depends:
base,
bifunctors,
Expand All @@ -218,6 +229,7 @@ test-suite CrossImpl
main-is: Reflex/Test/CrossImpl.hs
hs-source-dirs: test
ghc-options: -O2 -Wall -rtsopts
ghc-prof-options: -fprof-auto-calls
build-depends:
base,
containers,
Expand Down Expand Up @@ -285,12 +297,15 @@ test-suite DebugCycles
, hspec
, lens
, mtl
, primitive
, proctest
, ref-tf
, ref-tf
, reflex
, reflex
, these
, transformers
, reflex
, ref-tf
, witherable
, proctest


if flag(split-these)
Expand All @@ -314,6 +329,7 @@ test-suite RequesterT
, dependent-sum
, lens
, mtl
, primitive
, ref-tf
, reflex
, text
Expand All @@ -329,7 +345,6 @@ test-suite RequesterT
Test.Run

test-suite Adjustable
default-language: Haskell2010
type: exitcode-stdio-1.0
main-is: Adjustable.hs
hs-source-dirs: test
Expand Down Expand Up @@ -421,6 +436,7 @@ benchmark spider-bench
hs-source-dirs: bench test
main-is: Main.hs
ghc-options: -Wall -O2 -rtsopts
ghc-prof-options: -fprof-auto-calls
build-depends:
base,
containers,
Expand All @@ -447,6 +463,7 @@ benchmark saulzar-bench
c-sources: bench-cbits/checkCapability.c
main-is: RunAll.hs
ghc-options: -Wall -O2 -rtsopts -threaded
ghc-prof-options: -fprof-auto-calls
build-depends:
base,
containers,
Expand All @@ -469,6 +486,49 @@ benchmark saulzar-bench
Reflex.Plan.Reflex
Reflex.Bench.Focused

executable holddynchain
default-language: Haskell2010
hs-source-dirs: bench test
c-sources: bench-cbits/checkCapability.c
main-is: HoldDynChain.hs
ghc-options: -Wall -O2 -rtsopts -threaded
ghc-prof-options: -fprof-auto-calls
build-depends:
base,
containers,
criterion,
deepseq,
dependent-map,
dependent-sum,
loch-th,
mtl,
primitive,
process,
ref-tf,
reflex,
split,
stm,
time,
transformers,
patch,
these,
semialign,
witherable,
data-default,
semigroupoids,
monoidal-containers,
exception-transformers,
unbounded-delays,
lens,
random,
profunctors,
monad-control,
reflection
other-modules:
Reflex.TestPlan
Reflex.Plan.Reflex
Reflex.Bench.Focused

source-repository head
type: git
location: https://github.com/reflex-frp/reflex
60 changes: 60 additions & 0 deletions src/Data/List/Deferred.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
{-# LANGUAGE LambdaCase #-}
module Data.List.Deferred
( Deferred
, empty
, singleton
, toNonEmpty
, fromNonEmpty
, toList
) where

import Data.List.NonEmpty.Deferred.Internal (NonEmptyDeferred (..))
import qualified Data.List.NonEmpty.Deferred as NonEmpty

data Deferred a
= Deferred_Empty
| Deferred_Singleton a
| Deferred_Append !(NonEmptyDeferred a) !(NonEmptyDeferred a)

{-# INLINE toNonEmpty #-}
toNonEmpty :: Deferred a -> Maybe (NonEmptyDeferred a)
toNonEmpty = \case
Deferred_Empty -> Nothing
Deferred_Singleton a -> Just $ NonEmpty.singleton a
Deferred_Append a b -> Just $ a <> b

{-# INLINE fromNonEmpty #-}
fromNonEmpty :: NonEmptyDeferred a -> Deferred a
fromNonEmpty = \case
NonEmptyDeferred_Singleton a -> Deferred_Singleton a
NonEmptyDeferred_Append a b -> Deferred_Append a b

{-# INLINE empty #-}
empty :: Deferred a
empty = Deferred_Empty

{-# INLINE singleton #-}
singleton :: a -> Deferred a
singleton = fromNonEmpty . NonEmpty.singleton

{-# INLINE toList #-}
toList :: Deferred a -> [a]
toList = \case
Deferred_Empty -> []
Deferred_Singleton a -> [a]
Deferred_Append a b -> NonEmpty.toList $ a <> b

instance Semigroup (Deferred a) where
(<>) = \case
Deferred_Empty -> id
a@(Deferred_Singleton va) -> \case
Deferred_Empty -> a
Deferred_Singleton vb -> Deferred_Append (NonEmpty.singleton va) (NonEmpty.singleton vb)
Deferred_Append b1 b2 -> Deferred_Append (NonEmpty.singleton va) (b1 <> b2)
a@(Deferred_Append a1 a2) -> \case
Deferred_Empty -> a
Deferred_Singleton vb -> Deferred_Append (a1 <> a2) (NonEmpty.singleton vb)
Deferred_Append b1 b2 -> Deferred_Append (a1 <> a2) (b1 <> b2)

instance Monoid (Deferred a) where
mempty = Deferred_Empty
Loading