-- | This module contains helpers for benchmarking Haskell functions and IO actions.
module ParkBench.Benchmark
  ( whnf,
    whnfIO,
    measure,
  )
where

import qualified GHC.Stats as GHC
import ParkBench.BenchmarkInternal (whnf, whnfIO)
import ParkBench.Prelude
import ParkBench.RtsStats (RtsStats (RtsStats))
import ParkBench.Statistics (Timed (..))
import System.Mem (performGC)

-- | Measure the time/memory usage of an IO action.
measure :: IO () -> IO (Timed RtsStats)
measure :: IO () -> IO (Timed RtsStats)
measure IO ()
act = do
  IO ()
performGC
  RTSStats
s0 <- IO RTSStats
GHC.getRTSStats
  IO ()
act
  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) Word32 -> Word32 -> Bool
forall a. Eq a => a -> a -> Bool
== GCDetails -> Word32
GHC.gcdetails_gen (RTSStats -> GCDetails
GHC.gc RTSStats
s1)
      then RTSStats -> IO RTSStats
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 =
        RtsTime -> Rational
forall a. Real a => a -> Rational
toRational (RTSStats -> RtsTime
GHC.elapsed_ns RTSStats
s1 RtsTime -> RtsTime -> RtsTime
forall a. Num a => a -> a -> a
- RTSStats -> RtsTime
GHC.elapsed_ns RTSStats
s0)

  Timed RtsStats -> IO (Timed RtsStats)
forall (f :: * -> *) a. Applicative f => a -> f a
pure
    Timed :: forall a. Rational -> a -> Timed a
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
            (Word64 -> Rational
forall a. Real a => a -> Rational
toRational (RTSStats -> Word64
GHC.allocated_bytes RTSStats
s1 Word64 -> Word64 -> Word64
forall a. Num a => a -> a -> a
- RTSStats -> Word64
GHC.allocated_bytes RTSStats
s0))
            (Word64 -> Rational
forall a. Real a => a -> Rational
toRational (RTSStats -> Word64
GHC.copied_bytes RTSStats
s1 Word64 -> Word64 -> Word64
forall a. Num a => a -> a -> a
- RTSStats -> Word64
GHC.copied_bytes RTSStats
s0))
            (RtsTime -> Rational
forall a. Real a => a -> Rational
toRational (RTSStats -> RtsTime
GHC.cpu_ns RTSStats
s1 RtsTime -> RtsTime -> RtsTime
forall a. Num a => a -> a -> a
- RTSStats -> RtsTime
GHC.cpu_ns RTSStats
s0))
            (Word64 -> Rational
forall a. Real a => a -> Rational
toRational (RTSStats -> Word64
GHC.cumulative_live_bytes RTSStats
s2 Word64 -> Word64 -> Word64
forall a. Num a => a -> a -> a
- RTSStats -> Word64
GHC.cumulative_live_bytes RTSStats
s0))
            (Word64 -> Rational
forall a. Real a => a -> Rational
toRational (RTSStats -> Word64
GHC.cumulative_par_balanced_copied_bytes RTSStats
s1 Word64 -> Word64 -> Word64
forall a. Num a => a -> a -> a
- RTSStats -> Word64
GHC.cumulative_par_balanced_copied_bytes RTSStats
s0))
            Rational
elapsed_ns
            (RtsTime -> Rational
forall a. Real a => a -> Rational
toRational (RTSStats -> RtsTime
GHC.gc_cpu_ns RTSStats
s1 RtsTime -> RtsTime -> RtsTime
forall a. Num a => a -> a -> a
- RTSStats -> RtsTime
GHC.gc_cpu_ns RTSStats
s0))
            (RtsTime -> Rational
forall a. Real a => a -> Rational
toRational (RTSStats -> RtsTime
GHC.gc_elapsed_ns RTSStats
s1 RtsTime -> RtsTime -> RtsTime
forall a. Num a => a -> a -> a
- RTSStats -> RtsTime
GHC.gc_elapsed_ns RTSStats
s0))
            (Word32 -> Rational
forall a. Real a => a -> Rational
toRational (RTSStats -> Word32
GHC.gcs RTSStats
s1 Word32 -> Word32 -> Word32
forall a. Num a => a -> a -> a
- RTSStats -> Word32
GHC.gcs RTSStats
s0))
            (Word32 -> Rational
forall a. Real a => a -> Rational
toRational (RTSStats -> Word32
GHC.major_gcs RTSStats
s1 Word32 -> Word32 -> Word32
forall a. Num a => a -> a -> a
- RTSStats -> Word32
GHC.major_gcs RTSStats
s0))
            (Word64 -> Rational
forall a. Real a => a -> Rational
toRational (Word64 -> Word64 -> Word64
forall a. Ord a => a -> a -> a
max (RTSStats -> Word64
GHC.max_compact_bytes RTSStats
s2) (RTSStats -> Word64
GHC.max_compact_bytes RTSStats
s0)))
            (Word64 -> Rational
forall a. Real a => a -> Rational
toRational (Word64 -> Word64 -> Word64
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)))
            (Word64 -> Rational
forall a. Real a => a -> Rational
toRational (Word64 -> Word64 -> Word64
forall a. Ord a => a -> a -> a
max (RTSStats -> Word64
GHC.max_live_bytes RTSStats
s2) (RTSStats -> Word64
GHC.max_live_bytes RTSStats
s0)))
            (Word64 -> Rational
forall a. Real a => a -> Rational
toRational (Word64 -> Word64 -> Word64
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)))
            (Word64 -> Rational
forall a. Real a => a -> Rational
toRational (Word64 -> Word64 -> Word64
forall a. Ord a => a -> a -> a
max (RTSStats -> Word64
GHC.max_slop_bytes RTSStats
s2) (RTSStats -> Word64
GHC.max_slop_bytes RTSStats
s0)))
            (RtsTime -> Rational
forall a. Real a => a -> Rational
toRational (RTSStats -> RtsTime
GHC.mutator_cpu_ns RTSStats
s1 RtsTime -> RtsTime -> RtsTime
forall a. Num a => a -> a -> a
- RTSStats -> RtsTime
GHC.mutator_cpu_ns RTSStats
s0))
            (RtsTime -> Rational
forall a. Real a => a -> Rational
toRational (RTSStats -> RtsTime
GHC.mutator_elapsed_ns RTSStats
s1 RtsTime -> RtsTime -> RtsTime
forall a. Num a => a -> a -> a
- RTSStats -> RtsTime
GHC.mutator_elapsed_ns RTSStats
s0))
            (Word64 -> Rational
forall a. Real a => a -> Rational
toRational (RTSStats -> Word64
GHC.par_copied_bytes RTSStats
s1 Word64 -> Word64 -> Word64
forall a. Num a => a -> a -> a
- RTSStats -> Word64
GHC.par_copied_bytes RTSStats
s0))
      }