{-# LANGUAGE EmptyDataDecls #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE InstanceSigs #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE RankNTypes #-}
module Reflex.Profiled where
import Control.Monad
import Control.Monad.Exception
import Control.Monad.Fix
import Control.Monad.Primitive
import Control.Monad.Reader
import Control.Monad.Ref
import Control.Monad.State.Strict (StateT, execStateT, modify)
import Data.Coerce
import Data.Dependent.Map (DMap, GCompare)
import Data.FastMutableIntMap
import Data.IORef
import Data.List
import Data.Map (Map)
import qualified Data.Map.Strict as Map
import Data.Monoid ((<>))
import Data.Ord
import Data.Profunctor.Unsafe ((#.))
import qualified Data.Semigroup as S
import Data.Type.Coercion
import Foreign.Ptr
import GHC.Foreign
import GHC.IO.Encoding
import GHC.Stack
import Reflex.Class
import Reflex.Host.Class
import Reflex.PerformEvent.Class
import System.IO.Unsafe
import Unsafe.Coerce
data ProfiledTimeline t
{-# NOINLINE profilingData #-}
profilingData :: IORef (Map (Ptr CostCentreStack) Int)
profilingData = unsafePerformIO $ newIORef Map.empty
data CostCentreTree = CostCentreTree
{ _costCentreTree_ownEntries :: !Int
, _costCentreTree_cumulativeEntries :: !Int
, _costCentreTree_children :: !(Map (Ptr CostCentre) CostCentreTree)
}
deriving (Show, Eq, Ord)
instance S.Semigroup CostCentreTree where
(CostCentreTree oa ea ca) <> (CostCentreTree ob eb cb) =
CostCentreTree (oa + ob) (ea + eb) $ Map.unionWith (S.<>) ca cb
instance Monoid CostCentreTree where
mempty = CostCentreTree 0 0 mempty
mappend = (S.<>)
getCostCentreStack :: Ptr CostCentreStack -> IO [Ptr CostCentre]
getCostCentreStack = go []
where go l ccs = if ccs == nullPtr
then return l
else do
cc <- ccsCC ccs
parent <- ccsParent ccs
go (cc : l) parent
toCostCentreTree :: Ptr CostCentreStack -> Int -> IO CostCentreTree
toCostCentreTree ccs n =
foldr (\cc child -> CostCentreTree 0 n $ Map.singleton cc child) (CostCentreTree n n mempty)
<$> getCostCentreStack ccs
getCostCentreTree :: IO CostCentreTree
getCostCentreTree = do
vals <- readIORef profilingData
mconcat <$> mapM (uncurry toCostCentreTree) (Map.toList vals)
formatCostCentreTree :: CostCentreTree -> IO String
formatCostCentreTree cct0 = unlines . reverse <$> execStateT (go 0 cct0) []
where go :: Int -> CostCentreTree -> StateT [String] IO ()
go depth cct = do
let children = sortOn (Down . _costCentreTree_cumulativeEntries . snd) $ Map.toList $ _costCentreTree_children cct
indent = mconcat $ replicate depth " "
forM_ children $ \(cc, childCct) -> do
lbl <- liftIO $ peekCString utf8 =<< ccLabel cc
mdl <- liftIO $ peekCString utf8 =<< ccModule cc
loc <- liftIO $ peekCString utf8 =<< ccSrcSpan cc
let description = mdl <> "." <> lbl <> " (" <> loc <> ")"
modify $ (:) $ indent <> description <> "\t" <> show (_costCentreTree_cumulativeEntries childCct) <> "\t" <> show (_costCentreTree_ownEntries childCct)
go (succ depth) childCct
showProfilingData :: IO ()
showProfilingData = do
putStr =<< formatCostCentreTree =<< getCostCentreTree
writeProfilingData :: FilePath -> IO ()
writeProfilingData p = do
writeFile p =<< formatCostCentreTree =<< getCostCentreTree
newtype ProfiledM m a = ProfiledM { runProfiledM :: m a }
deriving (Functor, Applicative, Monad, MonadFix, MonadException, MonadAsyncException)
profileEvent :: Reflex t => Event t a -> Event t a
profileEvent e = unsafePerformIO $ do
stack <- getCurrentCCS e
let f x = unsafePerformIO $ do
modifyIORef' profilingData $ Map.insertWith (+) stack 1
return $ return $ Just x
return $ pushCheap f e
instance Reflex t => Reflex (ProfiledTimeline t) where
newtype Behavior (ProfiledTimeline t) a = Behavior_Profiled { unBehavior_Profiled :: Behavior t a }
newtype Event (ProfiledTimeline t) a = Event_Profiled { unEvent_Profiled :: Event t a }
newtype Dynamic (ProfiledTimeline t) a = Dynamic_Profiled { unDynamic_Profiled :: Dynamic t a }
newtype Incremental (ProfiledTimeline t) p = Incremental_Profiled { unIncremental_Profiled :: Incremental t p }
type PushM (ProfiledTimeline t) = ProfiledM (PushM t)
type PullM (ProfiledTimeline t) = ProfiledM (PullM t)
never = Event_Profiled never
constant = Behavior_Profiled . constant
push f (Event_Profiled e) = coerce $ push (coerce f) $ profileEvent e
pushCheap f (Event_Profiled e) = coerce $ pushCheap (coerce f) $ profileEvent e
pull = Behavior_Profiled . pull . coerce
fanG (Event_Profiled e) = EventSelectorG $ coerce $ selectG (fanG $ profileEvent e)
mergeG :: forall (k :: z -> *) q v. GCompare k
=> (forall a. q a -> Event (ProfiledTimeline t) (v a))
-> DMap k q -> Event (ProfiledTimeline t) (DMap k v)
mergeG nt = Event_Profiled #. mergeG (coerce nt)
switch (Behavior_Profiled b) = coerce $ profileEvent $ switch (coerceBehavior b)
coincidence (Event_Profiled e) = coerce $ profileEvent $ coincidence (coerceEvent e)
current (Dynamic_Profiled d) = coerce $ current d
updated (Dynamic_Profiled d) = coerce $ profileEvent $ updated d
unsafeBuildDynamic (ProfiledM a0) (Event_Profiled a') = coerce $ unsafeBuildDynamic a0 a'
unsafeBuildIncremental (ProfiledM a0) (Event_Profiled a') = coerce $ unsafeBuildIncremental a0 a'
mergeIncrementalG nt res = Event_Profiled $ mergeIncrementalG (coerce nt) (coerce res)
mergeIncrementalWithMoveG nt res = Event_Profiled $ mergeIncrementalWithMoveG (coerce nt) (coerce res)
currentIncremental (Incremental_Profiled i) = coerce $ currentIncremental i
updatedIncremental (Incremental_Profiled i) = coerce $ profileEvent $ updatedIncremental i
incrementalToDynamic (Incremental_Profiled i) = coerce $ incrementalToDynamic i
behaviorCoercion c =
Coercion `trans` behaviorCoercion @t c `trans` Coercion
eventCoercion c =
Coercion `trans` eventCoercion @t c `trans` Coercion
dynamicCoercion c =
Coercion `trans` dynamicCoercion @t c `trans` Coercion
incrementalCoercion c d =
Coercion `trans` incrementalCoercion @t c d `trans` Coercion
mergeIntIncremental = Event_Profiled . mergeIntIncremental .
coerceWith (Coercion `trans` incrementalCoercion Coercion Coercion `trans` Coercion)
fanInt (Event_Profiled e) = coerce $ fanInt $ profileEvent e
deriving instance Functor (Dynamic t) => Functor (Dynamic (ProfiledTimeline t))
deriving instance Applicative (Dynamic t) => Applicative (Dynamic (ProfiledTimeline t))
deriving instance Monad (Dynamic t) => Monad (Dynamic (ProfiledTimeline t))
instance MonadHold t m => MonadHold (ProfiledTimeline t) (ProfiledM m) where
hold v0 (Event_Profiled v') = ProfiledM $ Behavior_Profiled <$> hold v0 v'
holdDyn v0 (Event_Profiled v') = ProfiledM $ Dynamic_Profiled <$> holdDyn v0 v'
holdIncremental v0 (Event_Profiled v') = ProfiledM $ Incremental_Profiled <$> holdIncremental v0 v'
buildDynamic (ProfiledM v0) (Event_Profiled v') = ProfiledM $ Dynamic_Profiled <$> buildDynamic v0 v'
headE (Event_Profiled e) = ProfiledM $ Event_Profiled <$> headE e
instance MonadSample t m => MonadSample (ProfiledTimeline t) (ProfiledM m) where
sample (Behavior_Profiled b) = ProfiledM $ sample b
instance MonadTrans ProfiledM where
lift = ProfiledM
instance MonadIO m => MonadIO (ProfiledM m) where
liftIO = lift . liftIO
instance PerformEvent t m => PerformEvent (ProfiledTimeline t) (ProfiledM m) where
type Performable (ProfiledM m) = Performable m
performEvent_ = lift . performEvent_ . coerce
performEvent = lift . fmap coerce . performEvent . coerce
instance MonadRef m => MonadRef (ProfiledM m) where
type Ref (ProfiledM m) = Ref m
newRef = lift . newRef
readRef = lift . readRef
writeRef r = lift . writeRef r
instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger (ProfiledTimeline t) (ProfiledM m) where
newEventWithTrigger = lift . fmap coerce . newEventWithTrigger
newFanEventWithTrigger f = do
es <- lift $ newFanEventWithTrigger f
return $ EventSelector $ \k -> coerce $ select es k
instance MonadReader r m => MonadReader r (ProfiledM m) where
ask = lift ask
local f (ProfiledM a) = ProfiledM $ local f a
reader = lift . reader
instance ReflexHost t => ReflexHost (ProfiledTimeline t) where
type EventTrigger (ProfiledTimeline t) = EventTrigger t
type EventHandle (ProfiledTimeline t) = EventHandle t
type HostFrame (ProfiledTimeline t) = ProfiledM (HostFrame t)
instance MonadSubscribeEvent t m => MonadSubscribeEvent (ProfiledTimeline t) (ProfiledM m) where
subscribeEvent = lift . subscribeEvent . coerce
instance PrimMonad m => PrimMonad (ProfiledM m) where
type PrimState (ProfiledM m) = PrimState m
primitive = lift . primitive
instance MonadReflexHost t m => MonadReflexHost (ProfiledTimeline t) (ProfiledM m) where
type ReadPhase (ProfiledM m) = ProfiledM (ReadPhase m)
fireEventsAndRead ts r = lift $ fireEventsAndRead ts $ coerce r
runHostFrame = lift . runHostFrame . coerce
instance MonadReadEvent t m => MonadReadEvent (ProfiledTimeline t) (ProfiledM m) where
readEvent = lift . fmap coerce . readEvent