{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
import Control.Monad.ST (ST, runST)
import Data.Int
import Data.Primitive (ByteArray (..), Prim, PrimArray (..))
import qualified Data.Primitive as P
import qualified Data.Primitive.Sort
import qualified GHC.Exts as E
import qualified GHC.OldList as L
import Gauge.Main
import System.Random (Random, mkStdGen, randoms)
import Type.Reflection (TypeRep, typeRep)
main :: IO ()
main =
defaultMain
[ bgroup
"contiguous"
[ benchType (typeRep :: TypeRep Int8) (primArrayToByteArray . sortInt8 . byteArrayToPrimArray)
, benchType (typeRep :: TypeRep Word) (primArrayToByteArray . sortWord . byteArrayToPrimArray)
]
, bgroup
"tagged-unique"
[ bench "mini" (whnf (\(k, v) -> evalPair (Data.Primitive.Sort.sortUniqueTagged k v)) (sizedInts Mini, sizedInts Mini))
, bench "tiny" (whnf (\(k, v) -> evalPair (Data.Primitive.Sort.sortUniqueTagged k v)) (sizedInts Tiny, sizedInts Tiny))
, bench "small" (whnf (\(k, v) -> evalPair (Data.Primitive.Sort.sortUniqueTagged k v)) (sizedInts Small, sizedInts Small))
]
]
-- It is useful to have this here with inlining disabled because it
-- makes it easy to inspect Core to see if GHC's specialization is
-- working like we expect it to.
sortInt8 :: PrimArray Int8 -> PrimArray Int8
{-# NOINLINE sortInt8 #-}
sortInt8 !x = Data.Primitive.Sort.sort @Int8 x
sortWord :: PrimArray Word -> PrimArray Word
{-# NOINLINE sortWord #-}
sortWord !x = Data.Primitive.Sort.sort @Word x
evalPair :: (PrimArray a, PrimArray b) -> ()
evalPair (!_, !_) = ()
primArrayToByteArray :: PrimArray a -> ByteArray
primArrayToByteArray (PrimArray x) = ByteArray x
byteArrayToPrimArray :: ByteArray -> PrimArray a
byteArrayToPrimArray (ByteArray x) = PrimArray x
data Size = Mini | Tiny | Small | Medium | Large | Gigantic
deriving (Enum, Bounded)
data Arrangement = Unsorted | Presorted | Reversed
deriving (Enum, Bounded)
allSizes :: [Size]
allSizes = [minBound .. maxBound]
allArrangements :: [Arrangement]
allArrangements = [minBound .. maxBound]
showSize :: Size -> String
showSize x = case x of
Mini -> "mini"
Tiny -> "tiny"
Small -> "small"
Medium -> "medium"
Large -> "large"
Gigantic -> "gigantic"
numSize :: Size -> Int
numSize x = case x of
Mini -> 10
Tiny -> 100
Small -> 1000
Medium -> 10000
Large -> 100000
Gigantic -> 1000000
sizedInts :: Size -> PrimArray Int
sizedInts x = case x of
Mini -> intsMini
Tiny -> intsTiny
Small -> intsSmall
Medium -> intsMedium
Large -> intsLarge
Gigantic -> intsGigantic
intsMini, intsTiny, intsSmall, intsMedium, intsLarge, intsGigantic :: PrimArray Int
intsMini = E.fromList (L.take 10 (randoms (mkStdGen 23) :: [Int]))
intsTiny = E.fromList (L.take 100 (randoms (mkStdGen 87) :: [Int]))
intsSmall = E.fromList (L.take 1000 (randoms (mkStdGen 19) :: [Int]))
intsMedium = E.fromList (L.take 10000 (randoms (mkStdGen 47) :: [Int]))
intsLarge = E.fromList (L.take 100000 (randoms (mkStdGen 53) :: [Int]))
intsGigantic = E.fromList (L.take 1000000 (randoms (mkStdGen 12) :: [Int]))
showArrangement :: Arrangement -> String
showArrangement x = case x of
Unsorted -> "unsorted"
Presorted -> "presorted"
Reversed -> "reversed"
buildArrangement ::
(Prim a, Num a, Random a, Enum a, Bounded a) =>
Arrangement ->
TypeRep a ->
Int ->
ByteArray
buildArrangement x = case x of
Unsorted -> unsorted
Presorted -> presorted
Reversed -> reversed
benchType ::
(Prim a, Num a, Random a, Enum a, Bounded a) =>
TypeRep a ->
(ByteArray -> ByteArray) ->
Benchmark
benchType rep sort =
bgroup
(show rep)
(map (\arrange -> benchArrangement rep arrange sort) allArrangements)
benchArrangement ::
(Prim a, Num a, Random a, Enum a, Bounded a) =>
TypeRep a ->
Arrangement ->
(ByteArray -> ByteArray) ->
Benchmark
benchArrangement rep arrange sort =
bgroup
(showArrangement arrange)
(map (\sz -> let arr = buildArrangement arrange rep (numSize sz) in benchSize arr sz sort) allSizes)
benchSize :: ByteArray -> Size -> (ByteArray -> ByteArray) -> Benchmark
benchSize arr sz sort =
bench (showSize sz) (whnf sort arr)
unsorted :: forall a. (Prim a, Random a) => TypeRep a -> Int -> ByteArray
unsorted _typ n =
byteArrayFromList
(L.take n (randoms (mkStdGen 42) :: [a]))
presorted :: forall a. (Prim a, Num a, Enum a, Bounded a) => TypeRep a -> Int -> ByteArray
presorted _typ n =
byteArrayFromList
(L.take n (iterate (+ 1) (minBound :: a)))
reversed ::
forall a.
(Prim a, Num a, Enum a, Bounded a) =>
TypeRep a ->
Int ->
ByteArray
reversed _typ n =
byteArrayFromList
(L.take n (iterate (subtract 1) (maxBound :: a)))
byteArrayFromList :: (Prim a) => [a] -> ByteArray
byteArrayFromList xs = byteArrayFromListN (L.length xs) xs
byteArrayFromListN :: forall a. (Prim a) => Int -> [a] -> ByteArray
byteArrayFromListN len vs = runST run
where
run :: forall s. ST s ByteArray
run = do
arr <- P.newByteArray (len * P.sizeOf (undefined :: a))
let go :: [a] -> Int -> ST s ()
go !xs !ix = case xs of
[] -> return ()
a : as -> do
P.writeByteArray arr ix a
go as (ix + 1)
go vs 0
P.unsafeFreezeByteArray arr