1{-# LANGUAGE EmptyDataDecls #-}
2{-# LANGUAGE FlexibleContexts #-}
3{-# LANGUAGE FlexibleInstances #-}
4{-# LANGUAGE GeneralizedNewtypeDeriving #-}
5{-# LANGUAGE InstanceSigs #-}
6{-# LANGUAGE MultiParamTypeClasses #-}
7{-# LANGUAGE ScopedTypeVariables #-}
8{-# LANGUAGE StandaloneDeriving #-}
9{-# LANGUAGE TypeFamilies #-}
10{-# LANGUAGE UndecidableInstances #-}
11{-# LANGUAGE PolyKinds #-}
12{-# LANGUAGE RankNTypes #-}
17-- This module contains an instance of the 'Reflex' class that provides
18-- profiling/cost-center information.
19module Reflex.Profiled where
22import Control.Monad.Exception
23import Control.Monad.Fix
24import Control.Monad.Primitive
25import Control.Monad.Reader
26import Control.Monad.Ref
27import Control.Monad.State.Strict (StateT, execStateT, modify)
29import Data.Dependent.Map (DMap, GCompare)
30import Data.FastMutableIntMap
34import qualified Data.Map.Strict as Map
35import Data.Monoid ((<>))
37import Data.Profunctor.Unsafe ((#.))
38import qualified Data.Semigroup as S
39import Data.Type.Coercion
45import Reflex.Host.Class
46import Reflex.PerformEvent.Class
48import System.IO.Unsafe
51data ProfiledTimeline t
53{-# NOINLINE profilingData #-}
54profilingData :: IORef (Map (Ptr CostCentreStack) Int)
55profilingData = unsafePerformIO $ newIORef Map.empty
57data CostCentreTree = CostCentreTree
58 { _costCentreTree_ownEntries :: !Int
59 , _costCentreTree_cumulativeEntries :: !Int
60 , _costCentreTree_children :: !(Map (Ptr CostCentre) CostCentreTree)
62 deriving (Show, Eq, Ord)
64instance S.Semigroup CostCentreTree where
65 (CostCentreTree oa ea ca) <> (CostCentreTree ob eb cb) =
66 CostCentreTree (oa + ob) (ea + eb) $ Map.unionWith (S.<>) ca cb
68instance Monoid CostCentreTree where
69 mempty = CostCentreTree 0 0 mempty
72getCostCentreStack :: Ptr CostCentreStack -> IO [Ptr CostCentre]
73getCostCentreStack = go []
74 where go l ccs = if ccs == nullPtr
78 parent <- ccsParent ccs
81toCostCentreTree :: Ptr CostCentreStack -> Int -> IO CostCentreTree
82toCostCentreTree ccs n = do
83 ccList <- getCostCentreStack ccs
84 return $ foldr (\cc child -> CostCentreTree 0 n $ Map.singleton cc child) (CostCentreTree n n mempty) ccList
86getCostCentreTree :: IO CostCentreTree
88 vals <- readIORef profilingData
89 mconcat <$> mapM (uncurry toCostCentreTree) (Map.toList vals)
91formatCostCentreTree :: CostCentreTree -> IO String
92formatCostCentreTree cct0 = unlines . reverse <$> execStateT (go 0 cct0) []
93 where go :: Int -> CostCentreTree -> StateT [String] IO ()
95 let children = sortOn (Down . _costCentreTree_cumulativeEntries . snd) $ Map.toList $ _costCentreTree_children cct
96 indent = mconcat $ replicate depth " "
97 forM_ children $ \(cc, childCct) -> do
98 lbl <- liftIO $ peekCString utf8 =<< ccLabel cc
99 mdl <- liftIO $ peekCString utf8 =<< ccModule cc
100 loc <- liftIO $ peekCString utf8 =<< ccSrcSpan cc
101 let description = mdl <> "." <> lbl <> " (" <> loc <> ")"
102 modify $ (:) $ indent <> description <> "\t" <> show (_costCentreTree_cumulativeEntries childCct) <> "\t" <> show (_costCentreTree_ownEntries childCct)
103 go (succ depth) childCct
105showProfilingData :: IO ()
106showProfilingData = do
107 putStr =<< formatCostCentreTree =<< getCostCentreTree
109writeProfilingData :: FilePath -> IO ()
110writeProfilingData p = do
111 writeFile p =<< formatCostCentreTree =<< getCostCentreTree
113newtype ProfiledM m a = ProfiledM { runProfiledM :: m a }
114 deriving (Functor, Applicative, Monad, MonadFix, MonadException, MonadAsyncException)
116profileEvent :: Reflex t => Event t a -> Event t a
117profileEvent e = unsafePerformIO $ do
118 stack <- getCurrentCCS e
119 let f x = unsafePerformIO $ do
120 modifyIORef' profilingData $ Map.insertWith (+) stack 1
121 return $ return $ Just x
122 return $ pushCheap f e
124--TODO: Instead of profiling just the input or output of each one, profile all the inputs and all the outputs
126instance Reflex t => Reflex (ProfiledTimeline t) where
127 newtype Behavior (ProfiledTimeline t) a = Behavior_Profiled { unBehavior_Profiled :: Behavior t a }
128 newtype Event (ProfiledTimeline t) a = Event_Profiled { unEvent_Profiled :: Event t a }
129 newtype Dynamic (ProfiledTimeline t) a = Dynamic_Profiled { unDynamic_Profiled :: Dynamic t a }
130 newtype Incremental (ProfiledTimeline t) p = Incremental_Profiled { unIncremental_Profiled :: Incremental t p }
131 type PushM (ProfiledTimeline t) = ProfiledM (PushM t)
132 type PullM (ProfiledTimeline t) = ProfiledM (PullM t)
133 never = Event_Profiled never
134 constant = Behavior_Profiled . constant
135 push f (Event_Profiled e) = coerce $ push (coerce f) $ profileEvent e -- Profile before rather than after; this way fanout won't count against us
136 pushCheap f (Event_Profiled e) = coerce $ pushCheap (coerce f) $ profileEvent e
137 pull = Behavior_Profiled . pull . coerce
138 mergeG :: forall (k :: z -> *) q v. GCompare k
139 => (forall a. q a -> Event (ProfiledTimeline t) (v a))
140 -> DMap k q -> Event (ProfiledTimeline t) (DMap k v)
141 mergeG nt = Event_Profiled #. mergeG (coerce nt)
142 fan (Event_Profiled e) = EventSelector $ coerce $ select (fan $ profileEvent e)
143 switch (Behavior_Profiled b) = coerce $ profileEvent $ switch (coerceBehavior b)
144 coincidence (Event_Profiled e) = coerce $ profileEvent $ coincidence (coerceEvent e)
145 current (Dynamic_Profiled d) = coerce $ current d
146 updated (Dynamic_Profiled d) = coerce $ profileEvent $ updated d
147 unsafeBuildDynamic (ProfiledM a0) (Event_Profiled a') = coerce $ unsafeBuildDynamic a0 a'
148 unsafeBuildIncremental (ProfiledM a0) (Event_Profiled a') = coerce $ unsafeBuildIncremental a0 a'
149 mergeIncrementalG nt = (Event_Profiled . coerce) #. mergeIncrementalG nt
150 mergeIncrementalWithMoveG nt = (Event_Profiled . coerce) #. mergeIncrementalWithMoveG nt
151 currentIncremental (Incremental_Profiled i) = coerce $ currentIncremental i
152 updatedIncremental (Incremental_Profiled i) = coerce $ profileEvent $ updatedIncremental i
153 incrementalToDynamic (Incremental_Profiled i) = coerce $ incrementalToDynamic i
154 behaviorCoercion (c :: Coercion a b) = case behaviorCoercion c :: Coercion (Behavior t a) (Behavior t b) of
155 Coercion -> unsafeCoerce (Coercion :: Coercion (Behavior (ProfiledTimeline t) a) (Behavior (ProfiledTimeline t) a)) --TODO: Figure out how to make this typecheck without the unsafeCoerce
156 eventCoercion (c :: Coercion a b) = case eventCoercion c :: Coercion (Event t a) (Event t b) of
157 Coercion -> unsafeCoerce (Coercion :: Coercion (Event (ProfiledTimeline t) a) (Event (ProfiledTimeline t) a)) --TODO: Figure out how to make this typecheck without the unsafeCoerce
158 dynamicCoercion (c :: Coercion a b) = case dynamicCoercion c :: Coercion (Dynamic t a) (Dynamic t b) of
159 Coercion -> unsafeCoerce (Coercion :: Coercion (Dynamic (ProfiledTimeline t) a) (Dynamic (ProfiledTimeline t) a)) --TODO: Figure out how to make this typecheck without the unsafeCoerce
160 mergeIntIncremental = Event_Profiled . mergeIntIncremental . (unsafeCoerce :: Incremental (ProfiledTimeline t) (PatchIntMap (Event (ProfiledTimeline t) a)) -> Incremental t (PatchIntMap (Event t a)))
161 fanInt (Event_Profiled e) = coerce $ fanInt $ profileEvent e
163deriving instance Functor (Dynamic t) => Functor (Dynamic (ProfiledTimeline t))
164deriving instance Applicative (Dynamic t) => Applicative (Dynamic (ProfiledTimeline t))
165deriving instance Monad (Dynamic t) => Monad (Dynamic (ProfiledTimeline t))
167instance MonadHold t m => MonadHold (ProfiledTimeline t) (ProfiledM m) where
168 hold v0 (Event_Profiled v') = ProfiledM $ Behavior_Profiled <$> hold v0 v'
169 holdDyn v0 (Event_Profiled v') = ProfiledM $ Dynamic_Profiled <$> holdDyn v0 v'
170 holdIncremental v0 (Event_Profiled v') = ProfiledM $ Incremental_Profiled <$> holdIncremental v0 v'
171 buildDynamic (ProfiledM v0) (Event_Profiled v') = ProfiledM $ Dynamic_Profiled <$> buildDynamic v0 v'
172 headE (Event_Profiled e) = ProfiledM $ Event_Profiled <$> headE e
174instance MonadSample t m => MonadSample (ProfiledTimeline t) (ProfiledM m) where
175 sample (Behavior_Profiled b) = ProfiledM $ sample b
177instance MonadTrans ProfiledM where
180instance MonadIO m => MonadIO (ProfiledM m) where
181 liftIO = lift . liftIO
183instance PerformEvent t m => PerformEvent (ProfiledTimeline t) (ProfiledM m) where
184 type Performable (ProfiledM m) = Performable m
185 performEvent_ = lift . performEvent_ . coerce
186 performEvent = lift . fmap coerce . performEvent . coerce
188instance MonadRef m => MonadRef (ProfiledM m) where
189 type Ref (ProfiledM m) = Ref m
190 newRef = lift . newRef
191 readRef = lift . readRef
192 writeRef r = lift . writeRef r
194instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger (ProfiledTimeline t) (ProfiledM m) where
195 newEventWithTrigger = lift . fmap coerce . newEventWithTrigger
196 newFanEventWithTrigger f = do
197 es <- lift $ newFanEventWithTrigger f
198 return $ EventSelector $ \k -> coerce $ select es k
200instance MonadReader r m => MonadReader r (ProfiledM m) where
202 local f (ProfiledM a) = ProfiledM $ local f a
203 reader = lift . reader
205instance ReflexHost t => ReflexHost (ProfiledTimeline t) where
206 type EventTrigger (ProfiledTimeline t) = EventTrigger t
207 type EventHandle (ProfiledTimeline t) = EventHandle t
208 type HostFrame (ProfiledTimeline t) = ProfiledM (HostFrame t)
210instance MonadSubscribeEvent t m => MonadSubscribeEvent (ProfiledTimeline t) (ProfiledM m) where
211 subscribeEvent = lift . subscribeEvent . coerce
213instance PrimMonad m => PrimMonad (ProfiledM m) where
214 type PrimState (ProfiledM m) = PrimState m
215 primitive = lift . primitive
217instance MonadReflexHost t m => MonadReflexHost (ProfiledTimeline t) (ProfiledM m) where
218 type ReadPhase (ProfiledM m) = ProfiledM (ReadPhase m)
219 fireEventsAndRead ts r = lift $ fireEventsAndRead ts $ coerce r
220 runHostFrame = lift . runHostFrame . coerce
222instance MonadReadEvent t m => MonadReadEvent (ProfiledTimeline t) (ProfiledM m) where
223 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