module ParkBench.Internal.Measure
  ( measure,
  )
where

import qualified GHC.Stats as GHC
import ParkBench.Internal.Prelude
import ParkBench.Internal.RtsStats (RtsStats (RtsStats))
import ParkBench.Internal.Statistics (Timed (..))
import System.Mem (performGC)

-- | Measure the time/memory usage of an IO action.
measure :: IO a -> IO (Timed RtsStats)
measure :: forall a. IO a -> IO (Timed RtsStats)
measure IO a
action = do
  IO ()
performGC
  RTSStats
s0 <- IO RTSStats
GHC.getRTSStats
  a
_ <- IO a
action
  RTSStats
s1 <- IO RTSStats
GHC.getRTSStats
  -- Perform a major GC to update a few stats that are only accurate after a major GC.
  -- But the latest GC before collecting `s1` might have happened to be major, so check that first.
  RTSStats
s2 <-
    if GCDetails -> Word32
GHC.gcdetails_gen (RTSStats -> GCDetails
GHC.gc RTSStats
s0) forall a. Eq a => a -> a -> Bool
== GCDetails -> Word32
GHC.gcdetails_gen (RTSStats -> GCDetails
GHC.gc RTSStats
s1)
      then forall (f :: * -> *) a. Applicative f => a -> f a
pure RTSStats
s1
      else do
        IO ()
performGC
        IO RTSStats
GHC.getRTSStats

  let elapsed_ns :: Rational
      elapsed_ns :: Rational
elapsed_ns =
        forall a. Real a => a -> Rational
toRational (RTSStats -> RtsTime
GHC.elapsed_ns RTSStats
s1 forall a. Num a => a -> a -> a
- RTSStats -> RtsTime
GHC.elapsed_ns RTSStats
s0)

  forall (f :: * -> *) a. Applicative f => a -> f a
pure
    Timed
      { $sel:nanoseconds:Timed :: Rational
nanoseconds = Rational
elapsed_ns,
        $sel:value:Timed :: RtsStats
value =
          Rational
-> Rational
-> Rational
-> Rational
-> Rational
-> Rational
-> Rational
-> Rational
-> Rational
-> Rational
-> Rational
-> Rational
-> Rational
-> Rational
-> Rational
-> Rational
-> Rational
-> Rational
-> RtsStats
RtsStats
            (forall a. Real a => a -> Rational
toRational (RTSStats -> Word64
GHC.allocated_bytes RTSStats
s1 forall a. Num a => a -> a -> a
- RTSStats -> Word64
GHC.allocated_bytes RTSStats
s0))
            (forall a. Real a => a -> Rational
toRational (RTSStats -> Word64
GHC.copied_bytes RTSStats
s1 forall a. Num a => a -> a -> a
- RTSStats -> Word64
GHC.copied_bytes RTSStats
s0))
            (forall a. Real a => a -> Rational
toRational (RTSStats -> RtsTime
GHC.cpu_ns RTSStats
s1 forall a. Num a => a -> a -> a
- RTSStats -> RtsTime
GHC.cpu_ns RTSStats
s0))
            (forall a. Real a => a -> Rational
toRational (RTSStats -> Word64
GHC.cumulative_live_bytes RTSStats
s2 forall a. Num a => a -> a -> a
- RTSStats -> Word64
GHC.cumulative_live_bytes RTSStats
s0))
            (forall a. Real a => a -> Rational
toRational (RTSStats -> Word64
GHC.cumulative_par_balanced_copied_bytes RTSStats
s1 forall a. Num a => a -> a -> a
- RTSStats -> Word64
GHC.cumulative_par_balanced_copied_bytes RTSStats
s0))
            Rational
elapsed_ns
            (forall a. Real a => a -> Rational
toRational (RTSStats -> RtsTime
GHC.gc_cpu_ns RTSStats
s1 forall a. Num a => a -> a -> a
- RTSStats -> RtsTime
GHC.gc_cpu_ns RTSStats
s0))
            (forall a. Real a => a -> Rational
toRational (RTSStats -> RtsTime
GHC.gc_elapsed_ns RTSStats
s1 forall a. Num a => a -> a -> a
- RTSStats -> RtsTime
GHC.gc_elapsed_ns RTSStats
s0))
            (forall a. Real a => a -> Rational
toRational (RTSStats -> Word32
GHC.gcs RTSStats
s1 forall a. Num a => a -> a -> a
- RTSStats -> Word32
GHC.gcs RTSStats
s0))
            (forall a. Real a => a -> Rational
toRational (RTSStats -> Word32
GHC.major_gcs RTSStats
s1 forall a. Num a => a -> a -> a
- RTSStats -> Word32
GHC.major_gcs RTSStats
s0))
            (forall a. Real a => a -> Rational
toRational (forall a. Ord a => a -> a -> a
max (RTSStats -> Word64
GHC.max_compact_bytes RTSStats
s2) (RTSStats -> Word64
GHC.max_compact_bytes RTSStats
s0)))
            (forall a. Real a => a -> Rational
toRational (forall a. Ord a => a -> a -> a
max (RTSStats -> Word64
GHC.max_large_objects_bytes RTSStats
s2) (RTSStats -> Word64
GHC.max_large_objects_bytes RTSStats
s0)))
            (forall a. Real a => a -> Rational
toRational (forall a. Ord a => a -> a -> a
max (RTSStats -> Word64
GHC.max_live_bytes RTSStats
s2) (RTSStats -> Word64
GHC.max_live_bytes RTSStats
s0)))
            (forall a. Real a => a -> Rational
toRational (forall a. Ord a => a -> a -> a
max (RTSStats -> Word64
GHC.max_mem_in_use_bytes RTSStats
s1) (RTSStats -> Word64
GHC.max_mem_in_use_bytes RTSStats
s2)))
            (forall a. Real a => a -> Rational
toRational (forall a. Ord a => a -> a -> a
max (RTSStats -> Word64
GHC.max_slop_bytes RTSStats
s2) (RTSStats -> Word64
GHC.max_slop_bytes RTSStats
s0)))
            (forall a. Real a => a -> Rational
toRational (RTSStats -> RtsTime
GHC.mutator_cpu_ns RTSStats
s1 forall a. Num a => a -> a -> a
- RTSStats -> RtsTime
GHC.mutator_cpu_ns RTSStats
s0))
            (forall a. Real a => a -> Rational
toRational (RTSStats -> RtsTime
GHC.mutator_elapsed_ns RTSStats
s1 forall a. Num a => a -> a -> a
- RTSStats -> RtsTime
GHC.mutator_elapsed_ns RTSStats
s0))
            (forall a. Real a => a -> Rational
toRational (RTSStats -> Word64
GHC.par_copied_bytes RTSStats
s1 forall a. Num a => a -> a -> a
- RTSStats -> Word64
GHC.par_copied_bytes RTSStats
s0))
      }