packages feed

cloudchor-0.1.0.0: bench/Benchmark.hs

{-# LANGUAGE BlockArguments     #-}
{-# LANGUAGE ParallelListComp   #-}
{-# LANGUAGE ImpredicativeTypes #-}

module Benchmark where

import Control.Concurrent
import Control.Monad(when)
import Data.Foldable(for_)
import Data.IORef
import Data.Proxy(Proxy(..))
import Data.Time
import Data.Traversable(for)
import GHC.TypeLits(KnownSymbol)

import Choreography hiding (cond, cond', cond_, cond_')
import Choreography qualified as C
import Choreography.Choreo
import Choreography.Network
import Choreography.Network.Local
import Choreography.Network.LocalMsgCount

-- Benchmarking infrastructure

-- The type of the `cond_'` function, to avoid writing it out multiple times.
-- NB: This is a more specific version of the original `cond'` function's type.
type Cond_' =
  forall m l a b
  . (Bounded a, Enum a, Show a, Read a, KnownSymbol l)
  => (Proxy l, Unwrap l -> m a)
  -> (a -> Choreo m (b @ l))
  -> Choreo m (b @ l)

httpConfig :: [LocTm] -> HttpConfig
httpConfig ls = mkHttpConfig [(l, ("localhost", port)) | l <- ls | port <- [3000..]]

data SomeBackend = forall b. Backend b => SomeBackend b

runWithBackend :: SomeBackend -> Choreo IO () -> LocTm -> IO ()
runWithBackend (SomeBackend b) = runChoreography b

mkBackendConfig :: String -> IORef Int -> [LocTm] -> IO SomeBackend
mkBackendConfig "http"  _ = pure . SomeBackend . httpConfig
mkBackendConfig "local" _ = fmap SomeBackend . mkLocalConfig
mkBackendConfig "count" msgCounter = fmap SomeBackend . mkLocalMsgCountConfig msgCounter
mkBackendConfig _ _ = error "Invalid backend"

timed :: IO () -> IO ()
timed io = do
  before <- getCurrentTime
  io
  after <- getCurrentTime
  print $ diffUTCTime after before

-- Run a choreography with both the original `cond'` and the non-broadcasting `cond_'` and report their execution time.
benchmarkChoreography :: String -> String -> [LocTm] -> (Cond_' -> Choreo IO ()) -> IO ()
benchmarkChoreography backend cond locs choreo = do
  let chor = choreo $ if cond == "full" then C.cond' else C.cond_'
  msgCounter <- newIORef 0
  someCfg <- mkBackendConfig backend msgCounter locs

  timed do
    mvars <- for locs \l -> do
      mvar <- newEmptyMVar

      forkFinally (runWithBackend someCfg chor l) \_ -> putMVar mvar ()
      pure mvar

    for_ mvars takeMVar

  when (backend == "count") do
    count <- readIORef msgCounter
    putStrLn $ "Sends: " <> show count