{-# OPTIONS_HADDOCK prune #-}
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE CPP #-}
module Censor.Meter (
Meter(..)
, wallClock
, Counter(..)
, MeterError(..)
, withCounter
, SplitMeter(..)
, withSplitMeter
) where
import Control.Exception (Exception, throwIO)
import Data.Word (Word64)
#if !defined(darwin_HOST_OS)
import GHC.Clock (getMonotonicTimeNSec)
#endif
#if defined(linux_HOST_OS)
import Control.Concurrent (runInBoundThread, rtsSupportsBoundThreads)
import Control.Exception (finally)
import Foreign.C.Types (CInt(..))
#endif
newtype Meter = Meter
{ Meter -> Int -> IO () -> IO Word64
measure :: Int -> IO () -> IO Word64
}
newtype SplitMeter = SplitMeter
{ SplitMeter -> Int -> IO () -> IO (Word64, Word64)
measureSplit :: Int -> IO () -> IO (Word64, Word64)
}
wallClock :: Meter
wallClock :: Meter
wallClock = (Int -> IO () -> IO Word64) -> Meter
Meter Int -> IO () -> IO Word64
measureWall
#if defined(darwin_HOST_OS)
foreign import ccall unsafe "censor_monotonic_ns"
monotonicNS :: IO Word64
#else
monotonicNS :: IO Word64
monotonicNS :: IO Word64
monotonicNS = IO Word64
getMonotonicTimeNSec
{-# INLINE monotonicNS #-}
#endif
measureWall :: Int -> IO () -> IO Word64
measureWall :: Int -> IO () -> IO Word64
measureWall !Int
k !IO ()
act = do
!t0 <- IO Word64
monotonicNS
rep k act
!t1 <- monotonicNS
pure $! t1 - t0
{-# INLINE measureWall #-}
rep :: Int -> IO () -> IO ()
rep :: Int -> IO () -> IO ()
rep !Int
k !IO ()
act = Int -> IO ()
forall {t}. (Eq t, Num t) => t -> IO ()
go Int
k
where
go :: t -> IO ()
go t
0 = () -> IO ()
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ()
go t
i = IO ()
act IO () -> IO () -> IO ()
forall a b. IO a -> IO b -> IO b
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> t -> IO ()
go (t
i t -> t -> t
forall a. Num a => a -> a -> a
- t
1)
{-# INLINE rep #-}
data Counter =
InstructionsRetired
| BranchesRetired
| Cycles
| RefCycles
| TaskClock
| BranchMisses
| CacheMisses
deriving (Counter -> Counter -> Bool
(Counter -> Counter -> Bool)
-> (Counter -> Counter -> Bool) -> Eq Counter
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: Counter -> Counter -> Bool
== :: Counter -> Counter -> Bool
$c/= :: Counter -> Counter -> Bool
/= :: Counter -> Counter -> Bool
Eq, Int -> Counter -> ShowS
[Counter] -> ShowS
Counter -> String
(Int -> Counter -> ShowS)
-> (Counter -> String) -> ([Counter] -> ShowS) -> Show Counter
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> Counter -> ShowS
showsPrec :: Int -> Counter -> ShowS
$cshow :: Counter -> String
show :: Counter -> String
$cshowList :: [Counter] -> ShowS
showList :: [Counter] -> ShowS
Show)
data MeterError =
Unsupported
| OpenFailed !Int
deriving (MeterError -> MeterError -> Bool
(MeterError -> MeterError -> Bool)
-> (MeterError -> MeterError -> Bool) -> Eq MeterError
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: MeterError -> MeterError -> Bool
== :: MeterError -> MeterError -> Bool
$c/= :: MeterError -> MeterError -> Bool
/= :: MeterError -> MeterError -> Bool
Eq, Int -> MeterError -> ShowS
[MeterError] -> ShowS
MeterError -> String
(Int -> MeterError -> ShowS)
-> (MeterError -> String)
-> ([MeterError] -> ShowS)
-> Show MeterError
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> MeterError -> ShowS
showsPrec :: Int -> MeterError -> ShowS
$cshow :: MeterError -> String
show :: MeterError -> String
$cshowList :: [MeterError] -> ShowS
showList :: [MeterError] -> ShowS
Show)
instance Exception MeterError
#if defined(linux_HOST_OS)
foreign import ccall unsafe "censor_perf_open"
c_perf_open :: CInt -> IO CInt
foreign import ccall unsafe "censor_perf_begin"
c_perf_begin :: CInt -> IO ()
foreign import ccall unsafe "censor_perf_end"
c_perf_end :: CInt -> IO Word64
foreign import ccall unsafe "censor_perf_close"
c_perf_close :: CInt -> IO ()
counterCode :: Counter -> CInt
counterCode :: Counter -> CInt
counterCode Counter
InstructionsRetired = CInt
0
counterCode Counter
BranchesRetired = CInt
1
counterCode Counter
Cycles = CInt
2
counterCode Counter
RefCycles = CInt
3
counterCode Counter
TaskClock = CInt
4
counterCode Counter
BranchMisses = CInt
5
counterCode Counter
CacheMisses = CInt
6
withCounter :: Counter -> (Meter -> IO a) -> IO a
withCounter :: forall a. Counter -> (Meter -> IO a) -> IO a
withCounter Counter
counter Meter -> IO a
k = Counter -> (CInt -> IO a) -> IO a
forall a. Counter -> (CInt -> IO a) -> IO a
withPerf Counter
counter (Meter -> IO a
k (Meter -> IO a) -> (CInt -> Meter) -> CInt -> IO a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. CInt -> Meter
meterFor)
withSplitMeter :: (SplitMeter -> IO a) -> IO a
withSplitMeter :: forall a. (SplitMeter -> IO a) -> IO a
withSplitMeter SplitMeter -> IO a
k = Counter -> (CInt -> IO a) -> IO a
forall a. Counter -> (CInt -> IO a) -> IO a
withPerf Counter
Cycles (SplitMeter -> IO a
k (SplitMeter -> IO a) -> (CInt -> SplitMeter) -> CInt -> IO a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. CInt -> SplitMeter
splitFor)
withPerf :: Counter -> (CInt -> IO a) -> IO a
withPerf :: forall a. Counter -> (CInt -> IO a) -> IO a
withPerf Counter
counter CInt -> IO a
k = IO a -> IO a
forall {a}. IO a -> IO a
onOneThread (IO a -> IO a) -> IO a -> IO a
forall a b. (a -> b) -> a -> b
$ do
fd <- CInt -> IO CInt
c_perf_open (Counter -> CInt
counterCode Counter
counter)
if fd < 0
then throwIO (OpenFailed (negate (fromIntegral fd)))
else k fd `finally` c_perf_close fd
where
onOneThread :: IO a -> IO a
onOneThread
| Bool
rtsSupportsBoundThreads = IO a -> IO a
forall {a}. IO a -> IO a
runInBoundThread
| Bool
otherwise = IO a -> IO a
forall a. a -> a
id
meterFor :: CInt -> Meter
meterFor :: CInt -> Meter
meterFor !CInt
fd = (Int -> IO () -> IO Word64) -> Meter
Meter ((Int -> IO () -> IO Word64) -> Meter)
-> (Int -> IO () -> IO Word64) -> Meter
forall a b. (a -> b) -> a -> b
$ \Int
k IO ()
act -> do
CInt -> IO ()
c_perf_begin CInt
fd
Int -> IO () -> IO ()
rep Int
k IO ()
act
CInt -> IO Word64
c_perf_end CInt
fd
{-# INLINE meterFor #-}
splitFor :: CInt -> SplitMeter
splitFor :: CInt -> SplitMeter
splitFor !CInt
fd = (Int -> IO () -> IO (Word64, Word64)) -> SplitMeter
SplitMeter ((Int -> IO () -> IO (Word64, Word64)) -> SplitMeter)
-> (Int -> IO () -> IO (Word64, Word64)) -> SplitMeter
forall a b. (a -> b) -> a -> b
$ \Int
k IO ()
act -> do
CInt -> IO ()
c_perf_begin CInt
fd
!t0 <- IO Word64
monotonicNS
rep k act
!t1 <- monotonicNS
!cy <- c_perf_end fd
pure (cy, t1 - t0)
{-# INLINE splitFor #-}
#else
withCounter :: Counter -> (Meter -> IO a) -> IO a
withCounter _ _ = throwIO Unsupported
withSplitMeter :: (SplitMeter -> IO a) -> IO a
withSplitMeter _ = throwIO Unsupported
#endif