packages feed

fragr-0.1.0.0: bench/Bench.hs

{-# LANGUAGE TypeFamilies #-}

{-| tasty-bench benchmarks: whole-frame build/compile/execute cycles, since
a frame graph is a single-use object.
-}
module Main (main) where

import Control.Monad (foldM)
import Data.Text (Text)
import Data.Text qualified as Text
import Test.Tasty.Bench (bench, bgroup, defaultMain, whnfIO)

import Fragr (Handle)
import Fragr qualified as FG

data Buffer = Buffer

instance FG.Resource Buffer where
  type Desc Buffer = Int
  type Alloc Buffer = ()
  type Ctx Buffer = ()
  createResource _ () = pure Buffer
  destroyResource _ () _ = pure ()

main :: IO ()
main =
  defaultMain
    [ bgroup
        "chain (build+compile+execute)"
        [ bench "10" $ whnfIO (chain 10)
        , bench "100" $ whnfIO (chain 100)
        , bench "1000" $ whnfIO (chain 1000)
        ]
    , bgroup
        "culled (build+compile)"
        [ bench "100" $ whnfIO (culled 100)
        , bench "1000" $ whnfIO (culled 1000)
        ]
    , bgroup
        "fan-out (build+compile+execute)"
        [ bench "100" $ whnfIO (fanOut 100)
        , bench "1000" $ whnfIO (fanOut 1000)
        ]
    ]

{- | A linear chain: every pass reads its predecessor's output and writes a
fresh resource of its own; the last pass has a side effect, so the whole
chain survives culling and executes.
-}
chain :: Int -> IO ()
chain n = do
  g <- FG.newFrameGraph
  h0 <- producer g 0
  hLast <-
    foldM
      ( \prev i ->
          FG.addPass
            g
            (passName i)
            do
              FG.read prev
              h <- FG.create @Buffer (bufName i) i
              FG.write h
            \_data -> pure ()
      )
      h0
      [1 .. n - 1]
  FG.addPass_
    g
    "present"
    do
      FG.read hLast
      FG.setSideEffect
    (pure ())
  FG.compile g
  FG.execute g () ()

-- | N independent passes whose outputs nobody reads: all culled.
culled :: Int -> IO ()
culled n = do
  g <- FG.newFrameGraph
  mapM_ (producer g) [0 .. n - 1]
  FG.compile g

-- | One producer, N side-effecting readers.
fanOut :: Int -> IO ()
fanOut n = do
  g <- FG.newFrameGraph
  h <- producer g 0
  mapM_
    ( \i ->
        FG.addPass_
          g
          (passName i)
          do
            FG.read h
            FG.setSideEffect
          (pure ())
    )
    [1 .. n]
  FG.compile g
  FG.execute g () ()

producer :: FG.FrameGraph () () -> Int -> IO (Handle Buffer)
producer g i =
  FG.addPass
    g
    (passName i)
    do
      h <- FG.create @Buffer (bufName i) i
      FG.write h
    \_data -> pure ()

passName :: Int -> Text
passName i = "pass" <> Text.pack (show i)

bufName :: Int -> Text
bufName i = "buf" <> Text.pack (show i)