{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE RecordWildCards #-}
import Control.DeepSeq
import Data.FindCycle
import Data.Foldable (find)
import Data.List (intercalate)
import Data.Maybe
import Data.Numbers.Primes
import GHC.Generics
import Test.Tasty.Bench
import Test.Tasty.Patterns.Printer
data CycleSpec = CycleSpec {cycMu, cycLambda, cycDelay :: Int}
instance Show CycleSpec where
show CycleSpec{..} =
intercalate
","
[ showParam "mu" cycMu
, showParam "lambda" cycLambda
, showParam "delay" cycDelay
]
where
showParam s v = concat [s, "=", show v]
data Cycle = Cycle {cycF :: Int -> Int, cycX0 :: Int}
deriving (Generic, NFData)
mkCycle :: CycleSpec -> Cycle
mkCycle CycleSpec{..} = Cycle (delayed cycDelay f) (g 0)
where
n = cycMu + cycLambda
m = fromJust $ find (> n) primes
a = 123457
b = 98765
g i = (a * i + b) `mod` m
f x
| i < n - 1 = g (i + 1)
| otherwise = g cycMu
where
i = (modInv a m * ((x - b) `mod` m)) `mod` m
modInv 1 _ = 1
modInv x y = (i * y + 1) `div` x
where
i = x - modInv (y `mod` x) x
{-# NOINLINE delayed #-}
delayed :: Int -> (a -> b) -> (a -> b)
delayed n f x = countTo n `seq` f x
where
countTo 0 = ()
countTo i = countTo (i - 1)
main :: IO ()
main = defaultMain [mapLeafBenchmarks compareBrent benchmark]
where
compareBrent (name : xs)
| name /= "brent" = bcompare (printAwkExpr (locateBenchmark ("brent" : xs)))
compareBrent _ = id
cycles :: [CycleSpec]
cycles =
[ CycleSpec mu lambda delay
| mu <- [0, 10000, 1000000]
, lambda <- [10000, 1000000]
, delay <- [0, 10, 100, 1000]
]
runners :: [(String, CycleFinder Int -> Cycle -> Benchmarkable)]
runners =
[ ("findCycle", nf . runAlg findCycle)
, ("findExtractCycle", nf . runAlg findCycleExtract)
, ("findExtractCycle+drop", nf . (dropLists .) . runAlg findCycleExtract)
,
( "unsafeFindCycleFromList"
, nf . runAlg (\alg -> (unsafeFindCycleFromList alg .) . iterate)
)
]
where
runAlg f alg Cycle{..} = f alg cycF cycX0
dropLists (a, b, _) = (a, b)
algs :: [(String, CycleFinder Int)]
algs =
[ ("brent", brent)
, ("floyd", floyd)
, ("nivash", nivash)
, ("naiveHashable", naiveHashable)
, ("naiveOrd", naiveOrd)
]
benchmark :: Benchmark
benchmark =
bgroup
"Data.FindCycle"
[ bgroup
rName
[ env (pure (mkCycle spec)) $ \cyc ->
bgroup
(show spec)
[ bench name (rf alg cyc)
| (name, alg) <- algs
]
| spec <- cycles
]
| (rName, rf) <- runners
]