1{-# LANGUAGE EmptyDataDecls #-}
2{-# LANGUAGE FlexibleInstances #-}
3{-# LANGUAGE GeneralizedNewtypeDeriving #-}
4{-# LANGUAGE InstanceSigs #-}
5{-# LANGUAGE MultiParamTypeClasses #-}
6{-# LANGUAGE ScopedTypeVariables #-}
7{-# LANGUAGE StandaloneDeriving #-}
8{-# LANGUAGE TypeFamilies #-}
9{-# LANGUAGE UndecidableInstances #-}
10module Reflex.Profiled where
12import Control.Lens hiding (children)
14import Control.Monad.Fix
15import Control.Monad.Primitive
16import Control.Monad.Reader
17import Control.Monad.Ref
18import Control.Monad.State.Strict (StateT, execStateT, modify)
20import Data.Dependent.Map (DMap, GCompare)
22import Data.FastMutableIntMap
25import qualified Data.Map.Strict as Map
28import Data.Type.Coercion
34import Reflex.PerformEvent.Class
35import Reflex.Host.Class
37import System.IO.Unsafe
40data ProfiledTimeline t
42{-# NOINLINE profilingData #-}
43profilingData :: IORef (Map (Ptr CostCentreStack) Int)
44profilingData = unsafePerformIO $ newIORef Map.empty
46data CostCentreTree = CostCentreTree
47 { _costCentreTree_ownEntries :: !Int
48 , _costCentreTree_cumulativeEntries :: !Int
49 , _costCentreTree_children :: !(Map (Ptr CostCentre) CostCentreTree)
51 deriving (Show, Eq, Ord)
53instance Monoid CostCentreTree where
54 mempty = CostCentreTree 0 0 mempty
55 CostCentreTree oa ea ca `mappend` CostCentreTree ob eb cb = CostCentreTree (oa + ob) (ea + eb) $ Map.unionWith (<>) ca cb
57getCostCentreStack :: Ptr CostCentreStack -> IO [Ptr CostCentre]
58getCostCentreStack = go []
59 where go l ccs = if ccs == nullPtr
63 parent <- ccsParent ccs
66toCostCentreTree :: Ptr CostCentreStack -> Int -> IO CostCentreTree
67toCostCentreTree ccs n = do
68 ccList <- getCostCentreStack ccs
69 return $ foldr (\cc child -> CostCentreTree 0 n $ Map.singleton cc child) (CostCentreTree n n mempty) ccList
71getCostCentreTree :: IO CostCentreTree
73 vals <- readIORef profilingData
74 mconcat <$> mapM (uncurry toCostCentreTree) (Map.toList vals)
76formatCostCentreTree :: CostCentreTree -> IO String
77formatCostCentreTree cct0 = unlines . reverse <$> execStateT (go 0 cct0) []
78 where go :: Int -> CostCentreTree -> StateT [String] IO ()
80 let children = sortOn (Down . _costCentreTree_cumulativeEntries . snd) $ Map.toList $ _costCentreTree_children cct
81 indent = mconcat $ replicate depth " "
82 forM_ children $ \(cc, childCct) -> do
83 lbl <- liftIO $ peekCString utf8 =<< ccLabel cc
84 mdl <- liftIO $ peekCString utf8 =<< ccModule cc
85 loc <- liftIO $ peekCString utf8 =<< ccSrcSpan cc
86 let description = mdl <> "." <> lbl <> " (" <> loc <> ")"
87 modify $ (:) $ indent <> description <> "\t" <> show (_costCentreTree_cumulativeEntries childCct) <> "\t" <> show (_costCentreTree_ownEntries childCct)
88 go (succ depth) childCct
90showProfilingData :: IO ()
92 putStr =<< formatCostCentreTree =<< getCostCentreTree
94writeProfilingData :: FilePath -> IO ()
95writeProfilingData p = do
96 writeFile p =<< formatCostCentreTree =<< getCostCentreTree
98newtype ProfiledM m a = ProfiledM { runProfiledM :: m a } deriving (Functor, Applicative, Monad, MonadFix)
100profileEvent :: Reflex t => Event t a -> Event t a
101profileEvent e = unsafePerformIO $ do
102 stack <- getCurrentCCS e
103 let f x = unsafePerformIO $ do
104 modifyIORef' profilingData $ Map.insertWith (+) stack 1
105 return $ return $ Just x
106 return $ pushCheap f e
108--TODO: Instead of profiling just the input or output of each one, profile all the inputs and all the outputs
110instance Reflex t => Reflex (ProfiledTimeline t) where
111 newtype Behavior (ProfiledTimeline t) a = Behavior_Profiled { unBehavior_Profiled :: Behavior t a }
112 newtype Event (ProfiledTimeline t) a = Event_Profiled { unEvent_Profiled :: Event t a }
113 newtype Dynamic (ProfiledTimeline t) a = Dynamic_Profiled { unDynamic_Profiled :: Dynamic t a }
114 newtype Incremental (ProfiledTimeline t) p = Incremental_Profiled { unIncremental_Profiled :: Incremental t p }
115 type PushM (ProfiledTimeline t) = ProfiledM (PushM t)
116 type PullM (ProfiledTimeline t) = ProfiledM (PullM t)
117 never = Event_Profiled never
118 constant = Behavior_Profiled . constant
119 push f (Event_Profiled e) = coerce $ push (coerce f) $ profileEvent e -- Profile before rather than after; this way fanout won't count against us
120 pushCheap f (Event_Profiled e) = coerce $ pushCheap (coerce f) $ profileEvent e
121 pull = Behavior_Profiled . pull . coerce
122 merge :: forall k. GCompare k => DMap k (Event (ProfiledTimeline t)) -> Event (ProfiledTimeline t) (DMap k Identity)
123 merge = Event_Profiled . merge . (unsafeCoerce :: DMap k (Event (ProfiledTimeline t)) -> DMap k (Event t))
124 fan (Event_Profiled e) = EventSelector $ coerce $ select (fan $ profileEvent e)
125 switch (Behavior_Profiled b) = coerce $ profileEvent $ switch (coerceBehavior b)
126 coincidence (Event_Profiled e) = coerce $ profileEvent $ coincidence (coerceEvent e)
127 current (Dynamic_Profiled d) = coerce $ current d
128 updated (Dynamic_Profiled d) = coerce $ profileEvent $ updated d
129 unsafeBuildDynamic (ProfiledM a0) (Event_Profiled a') = coerce $ unsafeBuildDynamic a0 a'
130 unsafeBuildIncremental (ProfiledM a0) (Event_Profiled a') = coerce $ unsafeBuildIncremental a0 a'
131 mergeIncremental = Event_Profiled . mergeIncremental . (unsafeCoerce :: Incremental (ProfiledTimeline t) (PatchDMap k (Event (ProfiledTimeline t))) -> Incremental t (PatchDMap k (Event t)))
132 mergeIncrementalWithMove = Event_Profiled . mergeIncrementalWithMove . (unsafeCoerce :: Incremental (ProfiledTimeline t) (PatchDMapWithMove k (Event (ProfiledTimeline t))) -> Incremental t (PatchDMapWithMove k (Event t)))
133 currentIncremental (Incremental_Profiled i) = coerce $ currentIncremental i
134 updatedIncremental (Incremental_Profiled i) = coerce $ profileEvent $ updatedIncremental i
135 incrementalToDynamic (Incremental_Profiled i) = coerce $ incrementalToDynamic i
136 behaviorCoercion (c :: Coercion a b) = case behaviorCoercion c :: Coercion (Behavior t a) (Behavior t b) of
137 Coercion -> unsafeCoerce (Coercion :: Coercion (Behavior (ProfiledTimeline t) a) (Behavior (ProfiledTimeline t) a)) --TODO: Figure out how to make this typecheck without the unsafeCoerce
138 eventCoercion (c :: Coercion a b) = case eventCoercion c :: Coercion (Event t a) (Event t b) of
139 Coercion -> unsafeCoerce (Coercion :: Coercion (Event (ProfiledTimeline t) a) (Event (ProfiledTimeline t) a)) --TODO: Figure out how to make this typecheck without the unsafeCoerce
140 dynamicCoercion (c :: Coercion a b) = case dynamicCoercion c :: Coercion (Dynamic t a) (Dynamic t b) of
141 Coercion -> unsafeCoerce (Coercion :: Coercion (Dynamic (ProfiledTimeline t) a) (Dynamic (ProfiledTimeline t) a)) --TODO: Figure out how to make this typecheck without the unsafeCoerce
142 mergeIntIncremental = Event_Profiled . mergeIntIncremental . (unsafeCoerce :: Incremental (ProfiledTimeline t) (PatchIntMap (Event (ProfiledTimeline t) a)) -> Incremental t (PatchIntMap (Event t a)))
143 fanInt (Event_Profiled e) = coerce $ fanInt $ profileEvent e
145deriving instance Functor (Dynamic t) => Functor (Dynamic (ProfiledTimeline t))
146deriving instance Applicative (Dynamic t) => Applicative (Dynamic (ProfiledTimeline t))
147deriving instance Monad (Dynamic t) => Monad (Dynamic (ProfiledTimeline t))
149instance MonadHold t m => MonadHold (ProfiledTimeline t) (ProfiledM m) where
150 hold v0 (Event_Profiled v') = ProfiledM $ Behavior_Profiled <$> hold v0 v'
151 holdDyn v0 (Event_Profiled v') = ProfiledM $ Dynamic_Profiled <$> holdDyn v0 v'
152 holdIncremental v0 (Event_Profiled v') = ProfiledM $ Incremental_Profiled <$> holdIncremental v0 v'
153 buildDynamic (ProfiledM v0) (Event_Profiled v') = ProfiledM $ Dynamic_Profiled <$> buildDynamic v0 v'
154 headE (Event_Profiled e) = ProfiledM $ Event_Profiled <$> headE e
156instance MonadSample t m => MonadSample (ProfiledTimeline t) (ProfiledM m) where
157 sample (Behavior_Profiled b) = ProfiledM $ sample b
159instance MonadTrans ProfiledM where
162instance MonadIO m => MonadIO (ProfiledM m) where
163 liftIO = lift . liftIO
165instance PerformEvent t m => PerformEvent (ProfiledTimeline t) (ProfiledM m) where
166 type Performable (ProfiledM m) = Performable m
167 performEvent_ = lift . performEvent_ . coerce
168 performEvent = lift . fmap coerce . performEvent . coerce
170instance MonadRef m => MonadRef (ProfiledM m) where
171 type Ref (ProfiledM m) = Ref m
172 newRef = lift . newRef
173 readRef = lift . readRef
174 writeRef r = lift . writeRef r
176instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger (ProfiledTimeline t) (ProfiledM m) where
177 newEventWithTrigger = lift . fmap coerce . newEventWithTrigger
178 newFanEventWithTrigger f = do
179 es <- lift $ newFanEventWithTrigger f
180 return $ EventSelector $ \k -> coerce $ select es k
182instance MonadReader r m => MonadReader r (ProfiledM m) where
184 local f (ProfiledM a) = ProfiledM $ local f a
185 reader = lift . reader
187instance ReflexHost t => ReflexHost (ProfiledTimeline t) where
188 type EventTrigger (ProfiledTimeline t) = EventTrigger t
189 type EventHandle (ProfiledTimeline t) = EventHandle t
190 type HostFrame (ProfiledTimeline t) = ProfiledM (HostFrame t)
192instance MonadSubscribeEvent t m => MonadSubscribeEvent (ProfiledTimeline t) (ProfiledM m) where
193 subscribeEvent = lift . subscribeEvent . coerce
195instance PrimMonad m => PrimMonad (ProfiledM m) where
196 type PrimState (ProfiledM m) = PrimState m
197 primitive = lift . primitive
199instance MonadReflexHost t m => MonadReflexHost (ProfiledTimeline t) (ProfiledM m) where
200 type ReadPhase (ProfiledM m) = ProfiledM (ReadPhase m)
201 fireEventsAndRead ts r = lift $ fireEventsAndRead ts $ coerce r
202 runHostFrame = lift . runHostFrame . coerce
204instance MonadReadEvent t m => MonadReadEvent (ProfiledTimeline t) (ProfiledM m) where
205 readEvent = lift . fmap coerce . readEvent
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