on develop help sign in
wip
commitadfc860e25b7f2199091cdd90f5619c6a2670937 authorRyan Trinkle <ryan.trinkle@gmail.com> dateTue 18 Oct 2016, 11:53 (10 years ago) parent8c7653e
7 files changed+83−36
.gitignore+2 default.nix+4 src/Reflex/DynamicWriter.hs+7−8 src/Reflex/Optimizer.hs+33 src/Reflex/PerformEvent/Base.hs+2−1 src/Reflex/PostBuild/Class.hs+1−2 src/Reflex/Spider/Internal.hs+34−25
.gitignore+2
@@ -32,3 +32,5 @@ hsenv.log
3232.#*
3333/shell.nix
3434/ghci-tmp
35+*.dump-*
36+*.verbose-core2core
default.nix+4
@@ -4,6 +4,7 @@
44, monad-control, mtl, primitive, ref-tf, semigroupoids
55, semigroups, split, stdenv, stm, syb, template-haskell
66, these, transformers, transformers-compat, prim-uniq
7+, ghc_reflex ? null
78}:
89mkDerivation {
910 pname = "reflex";
@@ -20,6 +21,9 @@ mkDerivation {
2021 base bifunctors containers deepseq dependent-map dependent-sum
2122 hlint mtl ref-tf split transformers
2223 ];
24+ buildDepends = [
25+ ghc_reflex
26+ ];
2327 homepage = "https://github.com/reflex-frp/reflex";
2428 description = "Higher-order Functional Reactive Programming";
2529 license = stdenv.lib.licenses.bsd3;
src/Reflex/DynamicWriter.hs+7−8
@@ -19,6 +19,7 @@ import Control.Monad.Reader
1919import Control.Monad.Ref
2020import Control.Monad.State.Strict
2121import Data.Dependent.Map (DMap, DSum (..))
22+import qualified Data.Dependent.Map as DMap
2223import Data.Functor.Misc
2324import Data.Map (Map)
2425import qualified Data.Map as Map
@@ -166,15 +167,13 @@ instance (MonadAdjust t m, MonadFix m, Monoid w, MonadHold t m, Reflex t) => Mon
166167 liftedResult0 = mapKeyValuePairsMonotonic (\(WrapArg k :=> Identity r) -> k :=> Identity (getValue r)) result0
167168 liftedResult' = ffor result' $ \(PatchDMap p) -> PatchDMap $
168169 mapKeyValuePairsMonotonic (\(WrapArg k :=> ComposeMaybe mr) -> k :=> ComposeMaybe (fmap (Identity . getValue . runIdentity) mr)) p
169− liftedWritten0 :: DMap (Const2 (Some k) (Dynamic t w)) Identity
170− liftedWritten0 = mapKeyValuePairsMonotonic (\(WrapArg k :=> Identity r) -> Const2 (Some.This k) :=> Identity (getWritten r)) result0
171− liftedWritten' = ffor result' $ \(PatchDMap p) -> PatchDMap $
172− mapKeyValuePairsMonotonic (\(WrapArg k :=> ComposeMaybe mr) -> Const2 (Some.This k) :=> ComposeMaybe (fmap (Identity . getWritten . runIdentity) mr)) p
173− --TODO: We should be able to improve the performance here in two ways
174− -- 1. Incrementally merging the Dynamics
175− -- 2. Incrementally updating the mconcat of the merged Dynamics
170+ liftedWritten0 :: Map (Some k) (Dynamic t w)
171+ liftedWritten0 = Map.fromDistinctAscList $ fmap (\(WrapArg k :=> Identity r) -> (Some.This k, getWritten r)) $ DMap.toList result0
172+ liftedWritten' = ffor result' $ \(PatchDMap p) -> PatchMap $
173+ Map.fromDistinctAscList $ fmap (\(WrapArg k :=> ComposeMaybe mr) -> (Some.This k, fmap (getWritten . runIdentity) mr)) $ DMap.toList p
174+ --TODO: We should be able to improve the performance here by incrementally updating the mconcat of the merged Dynamics
176175 i <- holdIncremental liftedWritten0 liftedWritten'
177− tellDyn $ join $ mconcat . Map.elems . dmapToMap <$> incrementalToDynamic i
176+ tellDyn $ fmap (mconcat . Map.elems) $ incrementalToDynamic $ mergeDynIncremental i
178177 return (liftedResult0, liftedResult')
179178
180179withDynamicWriterT :: (Monoid w, Reflex t, MonadHold t m, MonadFix m)
src/Reflex/Optimizer.hsnew file+33
@@ -0,0 +1,33 @@
1+{-# LANGUAGE CPP #-}
2+{-# LANGUAGE LambdaCase #-}
3+module Reflex.Optimizer (plugin) where
4+
5+#ifndef ghcjs_HOST_OS
6+
7+import GhcPlugins
8+import Control.Arrow
9+
10+plugin :: Plugin
11+plugin = defaultPlugin { installCoreToDos = install }
12+
13+install :: [CommandLineOption] -> [CoreToDo] -> CoreM [CoreToDo]
14+install [] p = return $ makeInlinable : p
15+
16+makeInlinable :: CoreToDo
17+makeInlinable = CoreDoPluginPass "MakeInlinable" $ \modGuts -> do
18+ let f v = setIdInfo v $ let i = idInfo v in
19+ setInlinePragInfo i $ let p = inlinePragInfo i in
20+ if isDefaultInlinePragma p
21+ then defaultInlinePragma { inl_inline = Inlinable }
22+ else p
23+ newBinds = flip map (mg_binds modGuts) $ \case
24+ NonRec b e -> NonRec (f b) e
25+ Rec bes -> Rec $ map (first f) bes
26+ return $ modGuts { mg_binds = newBinds }
27+
28+#else
29+
30+plugin :: ()
31+plugin = ()
32+
33+#endif
src/Reflex/PerformEvent/Base.hs+2−1
@@ -32,6 +32,7 @@ import Data.Dependent.Map (DMap, GCompare (..), Some)
3232import qualified Data.Dependent.Map as DMap
3333import Data.Dependent.Sum
3434import Data.Functor.Misc
35+import Data.Maybe
3536import Data.Semigroup
3637import qualified Data.Some as Some
3738import Data.These
@@ -211,7 +212,7 @@ sequenceDMapWithAdjustRequestTWith f (dm0 :: DMap k (RequestT t request response
211212 childRequestMap <- holdIncremental requests0 requests'
212213 RequestT $ modify $ (:) $ ffor (mergeIncremental childRequestMap) $ \m ->
213214 mconcat $ (\(Const2 _ :=> Identity reqs) -> reqs) <$> DMap.toList m
214−-- RequestT $ modify $ (:) $ coincidence $ ffor requests' $ \(PatchDMap p) -> mergeWith DMap.union $ catMaybes $ ffor (DMap.toList p) $ \(Const2 _ :=> ComposeMaybe me) -> me -- We could make it prompt like this, but this seems to be slower than just delaying the PostBuild event to allow this stuff to settle (which is more similar to the previous behavior anyway)
215+ RequestT $ modify $ (:) $ coincidence $ ffor requests' $ \(PatchDMap p) -> mergeWith DMap.union $ catMaybes $ ffor (DMap.toList p) $ \(Const2 _ :=> ComposeMaybe me) -> me
215216 return (result0, result')
216217
217218instance PerformEvent t m => PerformEvent t (RequestT t request response m) where
src/Reflex/PostBuild/Class.hs+1−2
@@ -98,7 +98,6 @@ instance (Reflex t, MonadHold t m, MonadFix m, MonadAdjust t m, PerformEvent t m
9898 postBuild <- getPostBuild
9999 let loweredDm0 = DMap.map (`runPostBuildT` postBuild) dm0
100100 rec (result0, result') <- lift $ sequenceDMapWithAdjust loweredDm0 loweredDm'
101− delayedResult' <- performEvent $ return () <$ result' -- Delaying this result seems to be faster than making RequestT (and thus PerformEvent) more prompt
102101 let loweredDm' = ffor dm' $ \(PatchDMap p) -> PatchDMap $
103− DMap.map (ComposeMaybe . fmap (\v -> runPostBuildT v =<< headE (void delayedResult')) . getComposeMaybe) p --TODO: Avoid doing this headE so many times; once per loweredDm' firing ought to be OK, but it's not totally trivial to do because result' might be firing at the same time, and we don't want *that* to be the postBuild occurrence
102+ DMap.map (ComposeMaybe . fmap (\v -> runPostBuildT v =<< headE (void result')) . getComposeMaybe) p --TODO: Avoid doing this headE so many times; once per loweredDm' firing ought to be OK, but it's not totally trivial to do because result' might be firing at the same time, and we don't want *that* to be the postBuild occurrence
104103 return (result0, result')
src/Reflex/Spider/Internal.hs+34−25
@@ -17,6 +17,7 @@
1717{-# LANGUAGE TypeFamilies #-}
1818{-# LANGUAGE TypeOperators #-}
1919{-# LANGUAGE UndecidableInstances #-}
20+{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
2021-- | This module is the implementation of the 'Spider' 'Reflex' engine. It uses
2122-- a graph traversal algorithm to propagate 'Event's and 'Behavior's.
2223module Reflex.Spider.Internal where
@@ -180,12 +181,23 @@ unsafeNodeId a = unsafePerformIO $ do
180181-- Event
181182--------------------------------------------------------------------------------
182183
183−newtype Event x a = Event { subscribeAndRead :: Subscriber x a -> EventM x (EventSubscription x, Maybe a) }
184+newtype Event x a = Event { unEvent :: Subscriber x a -> EventM x (EventSubscription x, Maybe a) }
185+
186+{-# INLINE subscribeAndRead #-}
187+subscribeAndRead :: Event x a -> Subscriber x a -> EventM x (EventSubscription x, Maybe a)
188+subscribeAndRead = unEvent
189+
190+{-# RULES "pushCheap/cacheEvent" forall f e. pushCheap f (cacheEvent e) = pushCheap f e #-}
191+{-# RULES "hold/cacheEvent" forall f e. hold f (cacheEvent e) = hold f e #-}
192+{- RULES
193+ "subscribeAndRead/cacheEvent" forall e. subscribeAndRead (cacheEvent e) = subscribeAndRead e
194+ #-}
184195
185196-- | Construct an 'Event' equivalent to that constructed by 'push', but with no
186197-- caching; if the computation function is very cheap, this is (much) more
187198-- efficient than 'push'
188−{-# INLINABLE pushCheap #-}
199+{- INLINABLE pushCheap #-}
200+{-# INLINE [1] pushCheap #-}
189201pushCheap :: HasSpiderTimeline x => (a -> ComputeM x (Maybe b)) -> Event x a -> Event x b
190202pushCheap f e = Event $ \sub -> do
191203 (subscription, occ) <- subscribeAndRead e $ sub
@@ -210,7 +222,7 @@ data CacheSubscribed x a
210222--TODO: Try a caching strategy where we subscribe directly to the parent when
211223--there's only one subscriber, and then build our own WeakBag only when a second
212224--subscriber joins
213−{-# INLINABLE cacheEvent #-}
225+{-# NOINLINE [1] cacheEvent #-}
214226cacheEvent :: HasSpiderTimeline x => Event x a -> Event x a
215227cacheEvent e = Event $ \sub -> {-# SCC "cacheEvent" #-} do
216228 let !mSubscribedRef = unsafeNewIORef e Nothing
@@ -588,7 +600,7 @@ behaviorPull !p = Behavior $ do
588600 askParentsRef >>= mapM_ (\r -> liftIO $ modifyIORef' r (SomeBehaviorSubscribed (BehaviorSubscribedPull subscribed) :))
589601 return a
590602
591−behaviorDyn :: (HasSpiderTimeline x, R.Patch p) => Dyn x p -> Behavior x (R.PatchTarget p)
603+behaviorDyn :: R.Patch p => Dyn x p -> Behavior x (R.PatchTarget p)
592604behaviorDyn !d = Behavior $ readHoldTracked =<< getDynHold d
593605
594606{-# INLINE readHoldTracked #-}
@@ -599,7 +611,7 @@ readHoldTracked h = do
599611 askParentsRef >>= mapM_ (\r -> liftIO $ modifyIORef' r (SomeBehaviorSubscribed (BehaviorSubscribedHold h) :))
600612 return result
601613
602−{-# SPECIALIZE readBehaviorUntracked :: HasSpiderTimeline x => Behavior x a -> BehaviorM x a #-}
614+{-# SPECIALIZE readBehaviorUntracked :: Behavior x a -> BehaviorM x a #-}
603615{-# SPECIALIZE readBehaviorUntracked :: HasSpiderTimeline x => Behavior x a -> EventM x a #-}
604616readBehaviorUntracked :: Defer (SomeHoldInit x) m => Behavior x a -> m a
605617readBehaviorUntracked b = do
@@ -693,7 +705,7 @@ instance HasSpiderTimeline x => Defer (SomeHoldInit x) (EventM x) where
693705 {-# INLINE getDeferralQueue #-}
694706 getDeferralQueue = asksEventEnv eventEnvHoldInits
695707
696−instance HasSpiderTimeline x => Defer (SomeHoldInit x) (BehaviorM x) where
708+instance Defer (SomeHoldInit x) (BehaviorM x) where
697709 {-# INLINE getDeferralQueue #-}
698710 getDeferralQueue = BehaviorM $ asks snd
699711
@@ -752,8 +764,8 @@ instance HasSpiderTimeline x => Defer (SomeResetCoincidence x) (EventM x) where
752764 getDeferralQueue = asksEventEnv eventEnvResetCoincidences
753765
754766-- Note: hold cannot examine its event until after the phase is over
755−{-# SPECIALIZE hold :: (HasSpiderTimeline x, R.Patch p) => R.PatchTarget p -> Event x p -> EventM x (Hold x p) #-}
756−{- SPECIALIZE hold :: (HasSpiderTimeline x, R.Patch p) => R.PatchTarget p -> Event x p -> ComputeM x (Hold x p) #-}
767+{- SPECIALIZE hold :: (HasSpiderTimeline x, R.Patch p) => R.PatchTarget p -> Event x p -> EventM x (Hold x p) #-}
768+{-# INLINE [1] hold #-}
757769hold :: (R.Patch p, Defer (SomeHoldInit x) m) => R.PatchTarget p -> Event x p -> m (Hold x p)
758770hold v0 e = do
759771 valRef <- liftIO $ newIORef v0
@@ -1050,17 +1062,9 @@ instance HasSpiderTimeline x => Functor (Event x) where
10501062instance Functor (Behavior x) where
10511063 fmap f = pull . fmap f . readBehaviorTracked
10521064
1053−{-# INLINABLE push #-} --TODO: If this is helpful, we can get rid of the unsafeNewIORef and use unsafePerformIO directly
1065+{-# INLINE push #-}
10541066push :: HasSpiderTimeline x => (a -> ComputeM x (Maybe b)) -> Event x a -> Event x b
10551067push f e = cacheEvent (pushCheap f e)
1056−{-
1057−push f e = eventPush $ Push
1058− { pushCompute = f
1059− , pushParent = e
1060− , pushSubscribed = unsafeNewIORef (f, e) Nothing --TODO: Does the use of the tuple here create unnecessary overhead?
1061− }
1062−-- DISABLED: {- RULES "push/push" forall f g e. push f (push g e) = push (maybe (return Nothing) f <=< g) e #-}
1063−-}
10641068
10651069{-# INLINABLE pull #-}
10661070pull :: BehaviorM x a -> Behavior x a
@@ -1089,8 +1093,8 @@ coincidence a = eventCoincidence $ Coincidence
10891093run :: forall x b. HasSpiderTimeline x => [DSum (RootTrigger x) Identity] -> ResultM x b -> SpiderHost x b
10901094run roots after = do
10911095 tracePropagate (Proxy :: Proxy x) $ "Running an event frame with " <> show (length roots) <> " events"
1092− spiderTimeline <- SpiderHost ask
1093− result <- SpiderHost $ lift $ withMVar (_spiderTimeline_lock spiderTimeline) $ \_ -> flip runReaderT spiderTimeline $ unSpiderHost $ runFrame $ do
1096+ t <- SpiderHost ask
1097+ result <- SpiderHost $ lift $ withMVar (_spiderTimeline_lock t) $ \_ -> flip runReaderT t $ unSpiderHost $ runFrame $ do
10941098 rootsToPropagate <- forM roots $ \r@(RootTrigger (_, occRef, k) :=> a) -> do
10951099 occBefore <- liftIO $ do
10961100 occBefore <- readIORef occRef
@@ -1485,7 +1489,11 @@ subscribeCoincidenceSubscribed subscribed sub = WeakBag.insert sub (coincidenceS
14851489
14861490{-# INLINE merge #-}
14871491merge :: forall k x. (HasSpiderTimeline x, GCompare k) => Dynamic x (R.PatchDMap k (Event x)) -> Event x (DMap k Identity)
1488−merge d = cacheEvent $ Event $ \sub -> do
1492+merge d = cacheEvent (mergeCheap d)
1493+
1494+{-# INLINE [1] mergeCheap #-}
1495+mergeCheap :: forall k x. (HasSpiderTimeline x, GCompare k) => Dynamic x (R.PatchDMap k (Event x)) -> Event x (DMap k Identity)
1496+mergeCheap d = Event $ \sub -> do
14891497 initialParents <- readBehaviorUntracked $ dynamicCurrent d
14901498 accumRef <- liftIO $ newIORef $ error "merge: accumRef not yet initialized"
14911499 heightRef <- liftIO $ newIORef $ error "merge: heightRef not yet initialized"
@@ -1660,7 +1668,7 @@ clearEventEnv (EventEnv toAssignRef holdInitRef mergeUpdateRef mergeInitRef toCl
16601668
16611669-- | Run an event action outside of a frame
16621670runFrame :: forall x a. HasSpiderTimeline x => EventM x a -> SpiderHost x a --TODO: This function also needs to hold the mutex
1663−runFrame a = SpiderHost $ ask >>= \spiderTimeline -> lift $ do
1671+runFrame a = SpiderHost $ ask >>= \_ -> lift $ do
16641672 let env = _spiderTimeline_eventEnv (spiderTimeline :: SpiderTimeline x)
16651673 let go = do
16661674 result <- a
@@ -1822,7 +1830,7 @@ data Global
18221830
18231831{-# NOINLINE globalSpiderTimeline #-}
18241832globalSpiderTimeline :: SpiderTimeline Global
1825−globalSpiderTimeline = unsafePerformIO unsafeNewSpiderTimeline
1833+!globalSpiderTimeline = unsafePerformIO unsafeNewSpiderTimeline
18261834
18271835-- | Stores all global data relevant to a particular Spider timeline; only one
18281836-- value should exist for each type @x@
@@ -1887,6 +1895,7 @@ instance HasSpiderTimeline x => Monad (R.Dynamic (SpiderTimeline x)) where
18871895 fail _ = error "Dynamic does not support 'fail'"
18881896
18891897instance HasSpiderTimeline x => R.Reflex (SpiderTimeline x) where
1898+ {-# SPECIALIZE instance R.Reflex (SpiderTimeline Global) #-}
18901899 newtype Behavior (SpiderTimeline x) a = SpiderBehavior { unSpiderBehavior :: Behavior x a }
18911900 newtype Event (SpiderTimeline x) a = SpiderEvent { unSpiderEvent :: Event x a }
18921901 newtype Dynamic (SpiderTimeline x) a = SpiderDynamic { unSpiderDynamic :: Dynamic x (Identity a) } -- deriving (Functor, Applicative, Monad)
@@ -1897,7 +1906,7 @@ instance HasSpiderTimeline x => R.Reflex (SpiderTimeline x) where
18971906 never = SpiderEvent eventNever
18981907 {-# INLINABLE constant #-}
18991908 constant = SpiderBehavior . behaviorConst
1900− {-# INLINABLE push #-}
1909+ {-# INLINE push #-}
19011910 push f = SpiderEvent . push (coerce f) . unSpiderEvent
19021911 {-# INLINABLE pull #-}
19031912 pull = SpiderBehavior . pull . coerce
@@ -1964,11 +1973,11 @@ instance HasSpiderTimeline x => R.MonadSample (SpiderTimeline x) (SpiderHost x)
19641973 {-# INLINABLE sample #-}
19651974 sample = runFrame . readBehaviorUntracked . unSpiderBehavior
19661975
1967−instance HasSpiderTimeline x => R.MonadSample (SpiderTimeline x) (SpiderPullM x) where
1976+instance R.MonadSample (SpiderTimeline x) (SpiderPullM x) where
19681977 {-# INLINABLE sample #-}
19691978 sample = coerce . readBehaviorTracked . unSpiderBehavior
19701979
1971−instance HasSpiderTimeline x => R.MonadSample (SpiderTimeline x) (SpiderPushM x) where
1980+instance R.MonadSample (SpiderTimeline x) (SpiderPushM x) where
19721981 {-# INLINABLE sample #-}
19731982 sample (SpiderBehavior b) = SpiderPushM $ readBehaviorUntracked b
19741983