pure-borrow-0.1.0.0: bench/qsort.hs
{-# LANGUAGE BlockArguments #-}
{-# LANGUAGE QualifiedDo #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE TypeApplications #-}
{-# OPTIONS_GHC -Wno-name-shadowing #-}
module Main (main) where
import Control.Concurrent (getNumCapabilities)
import Control.Concurrent.DivideConquer.Linear (
naiveDivideAndConquer,
qsort,
qsortDC,
qsortDC',
)
import Control.Functor.Linear qualified as Control
import Control.Monad.Borrow.Pure.BO
import Control.Syntax.DataFlow qualified as DataFlow
import Data.Proxy (Proxy (..))
import Data.Vector.Algorithms.Intro qualified as AI
import Data.Vector.Generic.Mutable.Linear.Borrow.Unrestricted qualified as VL
import Data.Vector.Unboxed qualified as V
import Prelude.Linear (dup, unur)
import Prelude.Linear qualified as PL
import System.Random.Stateful
import Test.Tasty (askOption, defaultMainWithIngredients)
import Test.Tasty.Bench
import Test.Tasty.Ingredients.Basic (includingOptions)
import Test.Tasty.Options
import Text.Read (readMaybe)
import Prelude as P
data Mode = Parallel Word | NaiveDC Int | Worksteal Int | Sequential | IntroSort
deriving (Show, Eq, Ord)
data BenchOpts = BenchOpts {numThreads :: !Int, sampleSize :: !Int}
deriving (Show, Eq, Ord)
qsortWith :: Mode -> V.Vector Int -> V.Vector Int
qsortWith IntroSort v = V.modify AI.sort v
qsortWith (Parallel budget) v =
unur PL.$ linearly \lin ->
DataFlow.do
(lin, l2) <- dup lin
runBO lin Control.do
(v, lend) <- borrowM (VL.fromVector v l2)
qsort budget v
Control.pure PL.$ VL.toVector Control.<$> reclaim' lend
qsortWith Sequential v =
unur PL.$ linearly \lin ->
DataFlow.do
(lin, l2) <- dup lin
runBO lin Control.do
(v, lend) <- borrowM (VL.fromVector v l2)
qsort 0 v
pureAfter (VL.toVector PL.$ reclaim lend)
qsortWith (NaiveDC p) v =
unur PL.$ linearly \lin ->
DataFlow.do
(lin, l2) <- dup lin
runBO lin Control.do
(v, lend) <- borrowM (VL.fromVector v l2)
Control.void PL.$ naiveDivideAndConquer (qsortDC' p) v
pureAfter (VL.toVector PL.$ reclaim lend)
qsortWith (Worksteal p) v =
unur PL.$ linearly \lin ->
DataFlow.do
(lin, l2) <- dup lin
runBO lin Control.do
(v, lend) <- borrowM (VL.fromVector v l2)
Control.void PL.$ qsortDC (mkStdGen 42) p 128 v
pureAfter (VL.toVector PL.$ reclaim lend)
data SampleSize = SampleSize Int
deriving (Show, Eq, Ord)
instance IsOption SampleSize where
defaultValue = SampleSize 32
parseValue s =
case readMaybe s of
Just n | n > 0, kMAX_SIZE `rem` n == 0 -> Just (SampleSize n)
_ -> Nothing
optionName = return "size"
optionHelp =
return "Number of linear size steps (positive divisor of 32768)"
main :: IO ()
main = do
numThreads <- getNumCapabilities
let customOpts = [Option (Proxy :: Proxy SampleSize)]
ingredients = includingOptions customOpts : benchIngredients
defaultMainWithIngredients ingredients $ askOption \(SampleSize sampleSize) ->
bgroup "All" $ benches BenchOpts {..}
benches :: BenchOpts -> [Benchmark]
benches BenchOpts {..} =
[ bgroup
"qsort"
[ env
( pure $ runStateGen_ (mkStdGen 42) \g -> do
V.replicateM size (uniformM g)
)
\vec ->
bgroup
(show size)
( [ bench "intro" $ nf (qsortWith IntroSort) vec
, bench "sequential" $ nf (qsortWith Sequential) vec
]
<> [ bench ("parallel (budget = " <> show n <> ")") $
nf (qsortWith $ Parallel n) vec
| n <- [4, 8, 16, 32]
]
<> [ bench ("parallel-dc (thresh = 128)") $
nf (qsortWith $ NaiveDC 128) vec
]
<> [ bench ("worksteal (workers = " <> show n <> ")") $
nf (qsortWith $ Worksteal n) vec
| n <- [2, 4 .. numThreads]
]
)
| i <- [0 .. sampleSize]
, let size = i * kMAX_SIZE `quot` sampleSize
]
]
kMAX_SIZE :: Int
kMAX_SIZE = 32 * 1024