{-# LANGUAGE CPP #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
module Test.Tasty.Bench
(
defaultMain
, Benchmark
, bench
, bgroup
, Benchmarkable
, nf
, whnf
, nfIO
, whnfIO
, nfAppIO
, whnfAppIO
, csvReporter
) where
import Control.Applicative
import Control.DeepSeq
import Control.Exception
import Control.Monad
import Data.Data (Typeable)
import Data.Int
import Data.List (intercalate)
import Data.Proxy
#if MIN_VERSION_base(4,6,0)
import GHC.Stats
#endif
import System.CPUTime
import System.Mem
import Test.Tasty hiding (defaultMain)
import qualified Test.Tasty
import Test.Tasty.Options
import Test.Tasty.Providers
import Text.Printf
import Test.Tasty.Runners
import Test.Tasty.Ingredients
import Test.Tasty.Ingredients.ConsoleReporter
import System.IO
newtype RelStDev = RelStDev { RelStDev -> Double
unRelStDev :: Double }
deriving (RelStDev -> RelStDev -> Bool
(RelStDev -> RelStDev -> Bool)
-> (RelStDev -> RelStDev -> Bool) -> Eq RelStDev
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: RelStDev -> RelStDev -> Bool
$c/= :: RelStDev -> RelStDev -> Bool
== :: RelStDev -> RelStDev -> Bool
$c== :: RelStDev -> RelStDev -> Bool
Eq, Eq RelStDev
Eq RelStDev
-> (RelStDev -> RelStDev -> Ordering)
-> (RelStDev -> RelStDev -> Bool)
-> (RelStDev -> RelStDev -> Bool)
-> (RelStDev -> RelStDev -> Bool)
-> (RelStDev -> RelStDev -> Bool)
-> (RelStDev -> RelStDev -> RelStDev)
-> (RelStDev -> RelStDev -> RelStDev)
-> Ord RelStDev
RelStDev -> RelStDev -> Bool
RelStDev -> RelStDev -> Ordering
RelStDev -> RelStDev -> RelStDev
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
min :: RelStDev -> RelStDev -> RelStDev
$cmin :: RelStDev -> RelStDev -> RelStDev
max :: RelStDev -> RelStDev -> RelStDev
$cmax :: RelStDev -> RelStDev -> RelStDev
>= :: RelStDev -> RelStDev -> Bool
$c>= :: RelStDev -> RelStDev -> Bool
> :: RelStDev -> RelStDev -> Bool
$c> :: RelStDev -> RelStDev -> Bool
<= :: RelStDev -> RelStDev -> Bool
$c<= :: RelStDev -> RelStDev -> Bool
< :: RelStDev -> RelStDev -> Bool
$c< :: RelStDev -> RelStDev -> Bool
compare :: RelStDev -> RelStDev -> Ordering
$ccompare :: RelStDev -> RelStDev -> Ordering
$cp1Ord :: Eq RelStDev
Ord, Int -> RelStDev -> ShowS
[RelStDev] -> ShowS
RelStDev -> String
(Int -> RelStDev -> ShowS)
-> (RelStDev -> String) -> ([RelStDev] -> ShowS) -> Show RelStDev
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [RelStDev] -> ShowS
$cshowList :: [RelStDev] -> ShowS
show :: RelStDev -> String
$cshow :: RelStDev -> String
showsPrec :: Int -> RelStDev -> ShowS
$cshowsPrec :: Int -> RelStDev -> ShowS
Show, Typeable)
instance IsOption RelStDev where
defaultValue :: RelStDev
defaultValue = Double -> RelStDev
RelStDev Double
5
parseValue :: String -> Maybe RelStDev
parseValue = (Double -> RelStDev) -> Maybe Double -> Maybe RelStDev
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap Double -> RelStDev
RelStDev (Maybe Double -> Maybe RelStDev)
-> (String -> Maybe Double) -> String -> Maybe RelStDev
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Maybe Double
forall a. Read a => String -> Maybe a
safeRead
optionName :: Tagged RelStDev String
optionName = String -> Tagged RelStDev String
forall (f :: * -> *) a. Applicative f => a -> f a
pure String
"stdev"
optionHelp :: Tagged RelStDev String
optionHelp = String -> Tagged RelStDev String
forall (f :: * -> *) a. Applicative f => a -> f a
pure String
"Target relative standard deviation of measurements in percents (5 by default). Large values correspond to fast and loose benchmarks, and small ones to long and precise. If it takes far too long, consider setting --timeout, which will interrupt benchmarks, potentially before reaching the target deviation."
newtype Benchmarkable = Benchmarkable { Benchmarkable -> Int64 -> IO ()
_unBenchmarkable :: Int64 -> IO () }
deriving (Typeable)
showPicos :: Integer -> String
showPicos :: Integer -> String
showPicos Integer
i
| Double
a Double -> Double -> Bool
forall a. Eq a => a -> a -> Bool
== Double
0 = String
"0"
| Double
a Double -> Double -> Bool
forall a. Ord a => a -> a -> Bool
< Double
995 = String -> Double -> String
forall r. PrintfType r => String -> r
printf String
"%3.0f ps" Double
t
| Double
a Double -> Double -> Bool
forall a. Ord a => a -> a -> Bool
< Double
995e1 = String -> Double -> String
forall r. PrintfType r => String -> r
printf String
"%3.1f ns" (Double
t Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/ Double
1e3)
| Double
a Double -> Double -> Bool
forall a. Ord a => a -> a -> Bool
< Double
995e3 = String -> Double -> String
forall r. PrintfType r => String -> r
printf String
"%3.0f ns" (Double
t Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/ Double
1e3)
| Double
a Double -> Double -> Bool
forall a. Ord a => a -> a -> Bool
< Double
995e4 = String -> Double -> String
forall r. PrintfType r => String -> r
printf String
"%3.1f μs" (Double
t Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/ Double
1e6)
| Double
a Double -> Double -> Bool
forall a. Ord a => a -> a -> Bool
< Double
995e6 = String -> Double -> String
forall r. PrintfType r => String -> r
printf String
"%3.0f μs" (Double
t Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/ Double
1e6)
| Double
a Double -> Double -> Bool
forall a. Ord a => a -> a -> Bool
< Double
995e7 = String -> Double -> String
forall r. PrintfType r => String -> r
printf String
"%3.1f ms" (Double
t Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/ Double
1e9)
| Double
a Double -> Double -> Bool
forall a. Ord a => a -> a -> Bool
< Double
995e9 = String -> Double -> String
forall r. PrintfType r => String -> r
printf String
"%3.0f ms" (Double
t Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/ Double
1e9)
| Bool
otherwise = String -> Double -> String
forall r. PrintfType r => String -> r
printf String
"%.1f s" (Double
t Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/ Double
1e12)
where
t, a :: Double
t :: Double
t = Integer -> Double
forall a. Num a => Integer -> a
fromInteger Integer
i
a :: Double
a = Double -> Double
forall a. Num a => a -> a
abs Double
t
showBytes :: Integer -> String
showBytes :: Integer -> String
showBytes Integer
i
| Double
a Double -> Double -> Bool
forall a. Ord a => a -> a -> Bool
< Double
1000 = String -> Double -> String
forall r. PrintfType r => String -> r
printf String
"%3.0f B " Double
t
| Double
a Double -> Double -> Bool
forall a. Ord a => a -> a -> Bool
< Double
10189 = String -> Double -> String
forall r. PrintfType r => String -> r
printf String
"%3.1f KB" (Double
t Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/ Double
1024)
| Double
a Double -> Double -> Bool
forall a. Ord a => a -> a -> Bool
< Double
1023488 = String -> Double -> String
forall r. PrintfType r => String -> r
printf String
"%3.0f KB" (Double
t Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/ Double
1024)
| Double
a Double -> Double -> Bool
forall a. Ord a => a -> a -> Bool
< Double
10433332 = String -> Double -> String
forall r. PrintfType r => String -> r
printf String
"%3.1f MB" (Double
t Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/ Double
1048576)
| Double
a Double -> Double -> Bool
forall a. Ord a => a -> a -> Bool
< Double
1048051712 = String -> Double -> String
forall r. PrintfType r => String -> r
printf String
"%3.0f MB" (Double
t Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/ Double
1048576)
| Double
a Double -> Double -> Bool
forall a. Ord a => a -> a -> Bool
< Double
10683731149 = String -> Double -> String
forall r. PrintfType r => String -> r
printf String
"%3.1f GB" (Double
t Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/ Double
1073741824)
| Double
a Double -> Double -> Bool
forall a. Ord a => a -> a -> Bool
< Double
1073204953088 = String -> Double -> String
forall r. PrintfType r => String -> r
printf String
"%3.0f GB" (Double
t Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/ Double
1073741824)
| Bool
otherwise = String -> Double -> String
forall r. PrintfType r => String -> r
printf String
"%.1f TB" (Double
t Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/ Double
1099511627776)
where
t, a :: Double
t :: Double
t = Integer -> Double
forall a. Num a => Integer -> a
fromInteger Integer
i
a :: Double
a = Double -> Double
forall a. Num a => a -> a
abs Double
t
data Measurement = Measurement
{ Measurement -> Integer
measTime :: !Integer
, Measurement -> Integer
measAllocs :: !Integer
, Measurement -> Integer
measCopied :: !Integer
}
data Estimate = Estimate
{ Estimate -> Measurement
estMean :: !Measurement
, Estimate -> Integer
estSigma :: !Integer
}
prettyEstimate :: Estimate -> String
prettyEstimate :: Estimate -> String
prettyEstimate (Estimate Measurement
m Integer
sigma) =
Integer -> String
showPicos (Measurement -> Integer
measTime Measurement
m) String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
" ± " String -> ShowS
forall a. [a] -> [a] -> [a]
++ Integer -> String
showPicos (Integer
2 Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
* Integer
sigma)
prettyEstimateWithGC :: Estimate -> String
prettyEstimateWithGC :: Estimate -> String
prettyEstimateWithGC (Estimate Measurement
m Integer
sigma) =
Integer -> String
showPicos (Measurement -> Integer
measTime Measurement
m) String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
" ± " String -> ShowS
forall a. [a] -> [a] -> [a]
++ Integer -> String
showPicos (Integer
2 Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
* Integer
sigma)
String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
", " String -> ShowS
forall a. [a] -> [a] -> [a]
++ Integer -> String
showBytes (Measurement -> Integer
measAllocs Measurement
m) String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
" allocated, "
String -> ShowS
forall a. [a] -> [a] -> [a]
++ Integer -> String
showBytes (Measurement -> Integer
measCopied Measurement
m) String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
" copied"
csvEstimate :: Estimate -> String
csvEstimate :: Estimate -> String
csvEstimate (Estimate Measurement
m Integer
sigma) = Integer -> String
forall a. Show a => a -> String
show (Measurement -> Integer
measTime Measurement
m) String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
"," String -> ShowS
forall a. [a] -> [a] -> [a]
++ Integer -> String
forall a. Show a => a -> String
show (Integer
2 Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
* Integer
sigma)
csvEstimateWithGC :: Estimate -> String
csvEstimateWithGC :: Estimate -> String
csvEstimateWithGC (Estimate Measurement
m Integer
sigma) = Integer -> String
forall a. Show a => a -> String
show (Measurement -> Integer
measTime Measurement
m) String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
"," String -> ShowS
forall a. [a] -> [a] -> [a]
++ Integer -> String
forall a. Show a => a -> String
show (Integer
2 Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
* Integer
sigma)
String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
"," String -> ShowS
forall a. [a] -> [a] -> [a]
++ Integer -> String
forall a. Show a => a -> String
show (Measurement -> Integer
measAllocs Measurement
m) String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
"," String -> ShowS
forall a. [a] -> [a] -> [a]
++ Integer -> String
forall a. Show a => a -> String
show (Measurement -> Integer
measCopied Measurement
m)
predict
:: Measurement
-> Measurement
-> Estimate
predict :: Measurement -> Measurement -> Estimate
predict (Measurement Integer
t1 Integer
a1 Integer
c1) (Measurement Integer
t2 Integer
a2 Integer
c2) = Estimate :: Measurement -> Integer -> Estimate
Estimate
{ estMean :: Measurement
estMean = Integer -> Integer -> Integer -> Measurement
Measurement Integer
t Integer
a Integer
c
, estSigma :: Integer
estSigma = Double -> Integer
forall a b. (RealFrac a, Integral b) => a -> b
truncate (Double -> Double
forall a. Floating a => a -> a
sqrt (Integer -> Double
forall a. Num a => Integer -> a
fromInteger Integer
d) :: Double)
}
where
sqr :: a -> a
sqr a
x = a
x a -> a -> a
forall a. Num a => a -> a -> a
* a
x
d :: Integer
d = Integer -> Integer
forall a. Num a => a -> a
sqr (Integer
t1 Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
- Integer
t) Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
+ Integer -> Integer
forall a. Num a => a -> a
sqr (Integer
t2 Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
- Integer
2 Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
* Integer
t)
t :: Integer
t = (Integer
t1 Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
+ Integer
2 Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
* Integer
t2) Integer -> Integer -> Integer
forall a. Integral a => a -> a -> a
`quot` Integer
5
a :: Integer
a = (Integer
a1 Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
+ Integer
2 Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
* Integer
a2) Integer -> Integer -> Integer
forall a. Integral a => a -> a -> a
`quot` Integer
5
c :: Integer
c = (Integer
c1 Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
+ Integer
2 Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
* Integer
c2) Integer -> Integer -> Integer
forall a. Integral a => a -> a -> a
`quot` Integer
5
predictPerturbed :: Measurement -> Measurement -> Estimate
predictPerturbed :: Measurement -> Measurement -> Estimate
predictPerturbed Measurement
t1 Measurement
t2 = Estimate :: Measurement -> Integer -> Estimate
Estimate
{ estMean :: Measurement
estMean = Estimate -> Measurement
estMean (Measurement -> Measurement -> Estimate
predict Measurement
t1 Measurement
t2)
, estSigma :: Integer
estSigma = Integer -> Integer -> Integer
forall a. Ord a => a -> a -> a
max
(Estimate -> Integer
estSigma (Measurement -> Measurement -> Estimate
predict (Measurement -> Measurement
lo Measurement
t1) (Measurement -> Measurement
hi Measurement
t2)))
(Estimate -> Integer
estSigma (Measurement -> Measurement -> Estimate
predict (Measurement -> Measurement
hi Measurement
t1) (Measurement -> Measurement
lo Measurement
t2)))
}
where
prec :: Integer
prec = Integer -> Integer -> Integer
forall a. Ord a => a -> a -> a
max Integer
cpuTimePrecision Integer
1000000000
hi :: Measurement -> Measurement
hi Measurement
meas = Measurement
meas { measTime :: Integer
measTime = Measurement -> Integer
measTime Measurement
meas Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
+ Integer
prec }
lo :: Measurement -> Measurement
lo Measurement
meas = Measurement
meas { measTime :: Integer
measTime = Measurement -> Integer
measTime Measurement
meas Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
- Integer
prec }
#if !MIN_VERSION_base(4,10,0)
getRTSStatsEnabled :: IO Bool
#if MIN_VERSION_base(4,6,0)
getRTSStatsEnabled = getGCStatsEnabled
#else
getRTSStatsEnabled = pure False
#endif
#endif
getAllocsAndCopied :: IO (Integer, Integer)
getAllocsAndCopied :: IO (Integer, Integer)
getAllocsAndCopied = do
Bool
enabled <- IO Bool
getRTSStatsEnabled
if Bool -> Bool
not Bool
enabled then (Integer, Integer) -> IO (Integer, Integer)
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Integer
0, Integer
0) else
#if MIN_VERSION_base(4,10,0)
(\RTSStats
s -> (Word64 -> Integer
forall a. Integral a => a -> Integer
toInteger (Word64 -> Integer) -> Word64 -> Integer
forall a b. (a -> b) -> a -> b
$ RTSStats -> Word64
allocated_bytes RTSStats
s, Word64 -> Integer
forall a. Integral a => a -> Integer
toInteger (Word64 -> Integer) -> Word64 -> Integer
forall a b. (a -> b) -> a -> b
$ RTSStats -> Word64
copied_bytes RTSStats
s)) (RTSStats -> (Integer, Integer))
-> IO RTSStats -> IO (Integer, Integer)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> IO RTSStats
getRTSStats
#elif MIN_VERSION_base(4,6,0)
(\s -> (toInteger $ bytesAllocated s, toInteger $ bytesCopied s)) <$> getGCStats
#else
pure (0, 0)
#endif
measureTime :: Int64 -> Benchmarkable -> IO Measurement
measureTime :: Int64 -> Benchmarkable -> IO Measurement
measureTime Int64
n (Benchmarkable Int64 -> IO ()
act) = do
IO ()
performGC
Integer
startTime <- IO Integer
getCPUTime
(Integer
startAllocs, Integer
startCopied) <- IO (Integer, Integer)
getAllocsAndCopied
Int64 -> IO ()
act Int64
n
Integer
endTime <- IO Integer
getCPUTime
(Integer
endAllocs, Integer
endCopied) <- IO (Integer, Integer)
getAllocsAndCopied
Measurement -> IO Measurement
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Measurement -> IO Measurement) -> Measurement -> IO Measurement
forall a b. (a -> b) -> a -> b
$ Measurement :: Integer -> Integer -> Integer -> Measurement
Measurement
{ measTime :: Integer
measTime = Integer
endTime Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
- Integer
startTime
, measAllocs :: Integer
measAllocs = Integer
endAllocs Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
- Integer
startAllocs
, measCopied :: Integer
measCopied = Integer
endCopied Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
- Integer
startCopied
}
measureTimeUntil :: Maybe Integer -> Double -> Benchmarkable -> IO Estimate
measureTimeUntil :: Maybe Integer -> Double -> Benchmarkable -> IO Estimate
measureTimeUntil Maybe Integer
timeout Double
targetRelStDev Benchmarkable
b = do
Measurement
t1 <- Int64 -> Benchmarkable -> IO Measurement
measureTime Int64
1 Benchmarkable
b
Int64 -> Measurement -> Integer -> IO Estimate
go Int64
1 Measurement
t1 Integer
0
where
go :: Int64 -> Measurement -> Integer -> IO Estimate
go :: Int64 -> Measurement -> Integer -> IO Estimate
go Int64
n Measurement
t1 Integer
sumOfTs = do
Measurement
t2 <- Int64 -> Benchmarkable -> IO Measurement
measureTime (Int64
2 Int64 -> Int64 -> Int64
forall a. Num a => a -> a -> a
* Int64
n) Benchmarkable
b
let Estimate (Measurement Integer
meanN Integer
allocN Integer
copiedN) Integer
sigmaN = Measurement -> Measurement -> Estimate
predictPerturbed Measurement
t1 Measurement
t2
isTimeoutSoon :: Bool
isTimeoutSoon = case Maybe Integer
timeout of
Maybe Integer
Nothing -> Bool
False
Just Integer
tmt -> (Integer
sumOfTs Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
+ Measurement -> Integer
measTime Measurement
t1 Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
+ Integer
3 Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
* Measurement -> Integer
measTime Measurement
t2) Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
* Integer
12 Integer -> Integer -> Bool
forall a. Ord a => a -> a -> Bool
>= Integer
tmt Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
* Integer
10
isStDevInTargetRange :: Bool
isStDevInTargetRange = Integer
sigmaN Integer -> Integer -> Bool
forall a. Ord a => a -> a -> Bool
< Double -> Integer
forall a b. (RealFrac a, Integral b) => a -> b
truncate (Double
targetRelStDev Double -> Double -> Double
forall a. Num a => a -> a -> a
* Integer -> Double
forall a. Num a => Integer -> a
fromInteger Integer
meanN)
scale :: Integer -> Integer
scale = (Integer -> Integer -> Integer
forall a. Integral a => a -> a -> a
`quot` Int64 -> Integer
forall a. Integral a => a -> Integer
toInteger Int64
n)
if Bool
isStDevInTargetRange Bool -> Bool -> Bool
|| Bool
isTimeoutSoon
then Estimate -> IO Estimate
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Estimate -> IO Estimate) -> Estimate -> IO Estimate
forall a b. (a -> b) -> a -> b
$ Measurement -> Integer -> Estimate
Estimate (Integer -> Integer -> Integer -> Measurement
Measurement (Integer -> Integer
scale Integer
meanN) (Integer -> Integer
scale Integer
allocN) (Integer -> Integer
scale Integer
copiedN)) (Integer -> Integer
scale Integer
sigmaN)
else Int64 -> Measurement -> Integer -> IO Estimate
go (Int64
2 Int64 -> Int64 -> Int64
forall a. Num a => a -> a -> a
* Int64
n) Measurement
t2 (Integer
sumOfTs Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
+ Measurement -> Integer
measTime Measurement
t1)
instance IsTest Benchmarkable where
testOptions :: Tagged Benchmarkable [OptionDescription]
testOptions = [OptionDescription] -> Tagged Benchmarkable [OptionDescription]
forall (f :: * -> *) a. Applicative f => a -> f a
pure [Proxy RelStDev -> OptionDescription
forall v. IsOption v => Proxy v -> OptionDescription
Option (Proxy RelStDev
forall k (t :: k). Proxy t
Proxy :: Proxy RelStDev), Proxy (Maybe CsvPath) -> OptionDescription
forall v. IsOption v => Proxy v -> OptionDescription
Option (Proxy (Maybe CsvPath)
forall k (t :: k). Proxy t
Proxy :: Proxy (Maybe CsvPath))]
run :: OptionSet -> Benchmarkable -> (Progress -> IO ()) -> IO Result
run OptionSet
opts Benchmarkable
b = IO Result -> (Progress -> IO ()) -> IO Result
forall a b. a -> b -> a
const (IO Result -> (Progress -> IO ()) -> IO Result)
-> IO Result -> (Progress -> IO ()) -> IO Result
forall a b. (a -> b) -> a -> b
$ case NumThreads -> Int
getNumThreads (OptionSet -> NumThreads
forall v. IsOption v => OptionSet -> v
lookupOption OptionSet
opts) of
Int
1 -> do
let targetRelStDev :: Double
targetRelStDev = RelStDev -> Double
unRelStDev (OptionSet -> RelStDev
forall v. IsOption v => OptionSet -> v
lookupOption OptionSet
opts) Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/ Double
100
timeout :: Maybe Integer
timeout = case OptionSet -> Timeout
forall v. IsOption v => OptionSet -> v
lookupOption OptionSet
opts of
Timeout
NoTimeout -> Maybe Integer
forall a. Maybe a
Nothing
Timeout Integer
micros String
_ -> Integer -> Maybe Integer
forall a. a -> Maybe a
Just (Integer -> Maybe Integer) -> Integer -> Maybe Integer
forall a b. (a -> b) -> a -> b
$ Integer
micros Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
* Integer
1000000
Bool
hasGCStats <- IO Bool
getRTSStatsEnabled
Estimate
est <- Maybe Integer -> Double -> Benchmarkable -> IO Estimate
measureTimeUntil Maybe Integer
timeout Double
targetRelStDev Benchmarkable
b
Result -> IO Result
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Result -> IO Result) -> Result -> IO Result
forall a b. (a -> b) -> a -> b
$ String -> Result
testPassed (String -> Result) -> String -> Result
forall a b. (a -> b) -> a -> b
$ case OptionSet -> Maybe CsvPath
forall v. IsOption v => OptionSet -> v
lookupOption OptionSet
opts of
Maybe CsvPath
Nothing -> (if Bool
hasGCStats then Estimate -> String
prettyEstimateWithGC else Estimate -> String
prettyEstimate) Estimate
est
Just CsvPath{} -> (if Bool
hasGCStats then Estimate -> String
csvEstimateWithGC else Estimate -> String
csvEstimate) Estimate
est
Int
_ -> Result -> IO Result
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Result -> IO Result) -> Result -> IO Result
forall a b. (a -> b) -> a -> b
$ String -> Result
testFailed String
"Benchmarks should be run in a single-threaded mode (--jobs 1)"
bench :: String -> Benchmarkable -> Benchmark
bench :: String -> Benchmarkable -> Benchmark
bench = String -> Benchmarkable -> Benchmark
forall t. IsTest t => String -> t -> Benchmark
singleTest
bgroup :: String -> [Benchmark] -> Benchmark
bgroup :: String -> [Benchmark] -> Benchmark
bgroup = String -> [Benchmark] -> Benchmark
testGroup
type Benchmark = TestTree
defaultMain :: [Benchmark] -> IO ()
defaultMain :: [Benchmark] -> IO ()
defaultMain = [Ingredient] -> Benchmark -> IO ()
Test.Tasty.defaultMainWithIngredients [Ingredient]
ingredients (Benchmark -> IO ())
-> ([Benchmark] -> Benchmark) -> [Benchmark] -> IO ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> [Benchmark] -> Benchmark
testGroup String
"All"
where
ingredients :: [Ingredient]
ingredients = [Ingredient
listingTests, Ingredient
csvReporter, Ingredient
consoleTestReporter]
funcToBench :: (b -> c) -> (a -> b) -> a -> Benchmarkable
funcToBench :: (b -> c) -> (a -> b) -> a -> Benchmarkable
funcToBench b -> c
frc = ((Int64 -> IO ()) -> Benchmarkable
Benchmarkable ((Int64 -> IO ()) -> Benchmarkable)
-> (a -> Int64 -> IO ()) -> a -> Benchmarkable
forall b c a. (b -> c) -> (a -> b) -> a -> c
.) ((a -> Int64 -> IO ()) -> a -> Benchmarkable)
-> ((a -> b) -> a -> Int64 -> IO ())
-> (a -> b)
-> a
-> Benchmarkable
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (a -> b) -> a -> Int64 -> IO ()
forall t t. (Ord t, Num t) => (t -> b) -> t -> t -> IO ()
go
where
go :: (t -> b) -> t -> t -> IO ()
go t -> b
f t
x t
n
| t
n t -> t -> Bool
forall a. Ord a => a -> a -> Bool
<= t
0 = () -> IO ()
forall (f :: * -> *) a. Applicative f => a -> f a
pure ()
| Bool
otherwise = do
c
_ <- c -> IO c
forall a. a -> IO a
evaluate (b -> c
frc (t -> b
f t
x))
(t -> b) -> t -> t -> IO ()
go t -> b
f t
x (t
n t -> t -> t
forall a. Num a => a -> a -> a
- t
1)
{-# INLINE funcToBench #-}
nf :: NFData b => (a -> b) -> a -> Benchmarkable
nf :: (a -> b) -> a -> Benchmarkable
nf = (b -> ()) -> (a -> b) -> a -> Benchmarkable
forall b c a. (b -> c) -> (a -> b) -> a -> Benchmarkable
funcToBench b -> ()
forall a. NFData a => a -> ()
rnf
{-# INLINE nf #-}
whnf :: (a -> b) -> a -> Benchmarkable
whnf :: (a -> b) -> a -> Benchmarkable
whnf = (b -> b) -> (a -> b) -> a -> Benchmarkable
forall b c a. (b -> c) -> (a -> b) -> a -> Benchmarkable
funcToBench b -> b
forall a. a -> a
id
{-# INLINE whnf #-}
ioToBench :: (b -> c) -> IO b -> Benchmarkable
ioToBench :: (b -> c) -> IO b -> Benchmarkable
ioToBench b -> c
frc IO b
act = (Int64 -> IO ()) -> Benchmarkable
Benchmarkable Int64 -> IO ()
forall t. (Ord t, Num t) => t -> IO ()
go
where
go :: t -> IO ()
go t
n
| t
n t -> t -> Bool
forall a. Ord a => a -> a -> Bool
<= t
0 = () -> IO ()
forall (f :: * -> *) a. Applicative f => a -> f a
pure ()
| Bool
otherwise = do
b
val <- IO b
act
c
_ <- c -> IO c
forall a. a -> IO a
evaluate (b -> c
frc b
val)
t -> IO ()
go (t
n t -> t -> t
forall a. Num a => a -> a -> a
- t
1)
{-# INLINE ioToBench #-}
nfIO :: NFData a => IO a -> Benchmarkable
nfIO :: IO a -> Benchmarkable
nfIO = (a -> ()) -> IO a -> Benchmarkable
forall b c. (b -> c) -> IO b -> Benchmarkable
ioToBench a -> ()
forall a. NFData a => a -> ()
rnf
{-# INLINE nfIO #-}
whnfIO :: NFData a => IO a -> Benchmarkable
whnfIO :: IO a -> Benchmarkable
whnfIO = (a -> a) -> IO a -> Benchmarkable
forall b c. (b -> c) -> IO b -> Benchmarkable
ioToBench a -> a
forall a. a -> a
id
{-# INLINE whnfIO #-}
ioFuncToBench :: (b -> c) -> (a -> IO b) -> a -> Benchmarkable
ioFuncToBench :: (b -> c) -> (a -> IO b) -> a -> Benchmarkable
ioFuncToBench b -> c
frc = ((Int64 -> IO ()) -> Benchmarkable
Benchmarkable ((Int64 -> IO ()) -> Benchmarkable)
-> (a -> Int64 -> IO ()) -> a -> Benchmarkable
forall b c a. (b -> c) -> (a -> b) -> a -> c
.) ((a -> Int64 -> IO ()) -> a -> Benchmarkable)
-> ((a -> IO b) -> a -> Int64 -> IO ())
-> (a -> IO b)
-> a
-> Benchmarkable
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (a -> IO b) -> a -> Int64 -> IO ()
forall t t. (Ord t, Num t) => (t -> IO b) -> t -> t -> IO ()
go
where
go :: (t -> IO b) -> t -> t -> IO ()
go t -> IO b
f t
x t
n
| t
n t -> t -> Bool
forall a. Ord a => a -> a -> Bool
<= t
0 = () -> IO ()
forall (f :: * -> *) a. Applicative f => a -> f a
pure ()
| Bool
otherwise = do
b
val <- t -> IO b
f t
x
c
_ <- c -> IO c
forall a. a -> IO a
evaluate (b -> c
frc b
val)
(t -> IO b) -> t -> t -> IO ()
go t -> IO b
f t
x (t
n t -> t -> t
forall a. Num a => a -> a -> a
- t
1)
{-# INLINE ioFuncToBench #-}
nfAppIO :: NFData b => (a -> IO b) -> a -> Benchmarkable
nfAppIO :: (a -> IO b) -> a -> Benchmarkable
nfAppIO = (b -> ()) -> (a -> IO b) -> a -> Benchmarkable
forall b c a. (b -> c) -> (a -> IO b) -> a -> Benchmarkable
ioFuncToBench b -> ()
forall a. NFData a => a -> ()
rnf
{-# INLINE nfAppIO #-}
whnfAppIO :: (a -> IO b) -> a -> Benchmarkable
whnfAppIO :: (a -> IO b) -> a -> Benchmarkable
whnfAppIO = (b -> b) -> (a -> IO b) -> a -> Benchmarkable
forall b c a. (b -> c) -> (a -> IO b) -> a -> Benchmarkable
ioFuncToBench b -> b
forall a. a -> a
id
{-# INLINE whnfAppIO #-}
newtype CsvPath = CsvPath { CsvPath -> String
_unCsvPath :: FilePath }
deriving (Typeable)
instance IsOption (Maybe CsvPath) where
defaultValue :: Maybe CsvPath
defaultValue = Maybe CsvPath
forall a. Maybe a
Nothing
parseValue :: String -> Maybe (Maybe CsvPath)
parseValue = Maybe CsvPath -> Maybe (Maybe CsvPath)
forall a. a -> Maybe a
Just (Maybe CsvPath -> Maybe (Maybe CsvPath))
-> (String -> Maybe CsvPath) -> String -> Maybe (Maybe CsvPath)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. CsvPath -> Maybe CsvPath
forall a. a -> Maybe a
Just (CsvPath -> Maybe CsvPath)
-> (String -> CsvPath) -> String -> Maybe CsvPath
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> CsvPath
CsvPath
optionName :: Tagged (Maybe CsvPath) String
optionName = String -> Tagged (Maybe CsvPath) String
forall (f :: * -> *) a. Applicative f => a -> f a
pure String
"csv"
optionHelp :: Tagged (Maybe CsvPath) String
optionHelp = String -> Tagged (Maybe CsvPath) String
forall (f :: * -> *) a. Applicative f => a -> f a
pure String
"File to write results in CSV format. If specified, suppresses console output"
csvReporter :: Ingredient
csvReporter :: Ingredient
csvReporter = [OptionDescription]
-> (OptionSet
-> Benchmark -> Maybe (StatusMap -> IO (Double -> IO Bool)))
-> Ingredient
TestReporter [Proxy (Maybe CsvPath) -> OptionDescription
forall v. IsOption v => Proxy v -> OptionDescription
Option (Proxy (Maybe CsvPath)
forall k (t :: k). Proxy t
Proxy :: Proxy (Maybe CsvPath))] ((OptionSet
-> Benchmark -> Maybe (StatusMap -> IO (Double -> IO Bool)))
-> Ingredient)
-> (OptionSet
-> Benchmark -> Maybe (StatusMap -> IO (Double -> IO Bool)))
-> Ingredient
forall a b. (a -> b) -> a -> b
$
\OptionSet
opts Benchmark
tree -> do
CsvPath String
path <- OptionSet -> Maybe CsvPath
forall v. IsOption v => OptionSet -> v
lookupOption OptionSet
opts
(StatusMap -> IO (Double -> IO Bool))
-> Maybe (StatusMap -> IO (Double -> IO Bool))
forall (f :: * -> *) a. Applicative f => a -> f a
pure ((StatusMap -> IO (Double -> IO Bool))
-> Maybe (StatusMap -> IO (Double -> IO Bool)))
-> (StatusMap -> IO (Double -> IO Bool))
-> Maybe (StatusMap -> IO (Double -> IO Bool))
forall a b. (a -> b) -> a -> b
$ \StatusMap
smap -> do
IO Handle -> (Handle -> IO ()) -> (Handle -> IO ()) -> IO ()
forall a b c. IO a -> (a -> IO b) -> (a -> IO c) -> IO c
bracket
(do
Handle
h <- String -> IOMode -> IO Handle
openFile String
path IOMode
WriteMode
Handle -> BufferMode -> IO ()
hSetBuffering Handle
h BufferMode
LineBuffering
Bool
hasGCStats <- IO Bool
getRTSStatsEnabled
Handle -> String -> IO ()
hPutStrLn Handle
h (String -> IO ()) -> String -> IO ()
forall a b. (a -> b) -> a -> b
$ String
"Name,Mean (ps),2*Stdev (ps)" String -> ShowS
forall a. [a] -> [a] -> [a]
++
(if Bool
hasGCStats then String
",Allocated,Copied" else String
"")
Handle -> IO Handle
forall (f :: * -> *) a. Applicative f => a -> f a
pure Handle
h
)
Handle -> IO ()
hClose
(\Handle
h -> TestOutput -> StatusMap -> IO ()
csvOutput (Handle -> OptionSet -> Benchmark -> TestOutput
buildCsvOutput Handle
h OptionSet
opts Benchmark
tree) StatusMap
smap)
(Double -> IO Bool) -> IO (Double -> IO Bool)
forall (f :: * -> *) a. Applicative f => a -> f a
pure ((Double -> IO Bool) -> IO (Double -> IO Bool))
-> (Double -> IO Bool) -> IO (Double -> IO Bool)
forall a b. (a -> b) -> a -> b
$ IO Bool -> Double -> IO Bool
forall a b. a -> b -> a
const ((Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
0) (Int -> Bool) -> (Statistics -> Int) -> Statistics -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Statistics -> Int
statFailures (Statistics -> Bool) -> IO Statistics -> IO Bool
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> StatusMap -> IO Statistics
computeStatistics StatusMap
smap)
buildCsvOutput :: Handle -> OptionSet -> TestTree -> TestOutput
buildCsvOutput :: Handle -> OptionSet -> Benchmark -> TestOutput
buildCsvOutput Handle
h = (((([String] -> TestOutput) -> [String] -> TestOutput
forall a b. (a -> b) -> a -> b
$ []) (([String] -> TestOutput) -> TestOutput)
-> (Ap ((->) [String]) TestOutput -> [String] -> TestOutput)
-> Ap ((->) [String]) TestOutput
-> TestOutput
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Ap ((->) [String]) TestOutput -> [String] -> TestOutput
forall (f :: * -> *) a. Ap f a -> f a
getApp) (Ap ((->) [String]) TestOutput -> TestOutput)
-> (Benchmark -> Ap ((->) [String]) TestOutput)
-> Benchmark
-> TestOutput
forall b c a. (b -> c) -> (a -> b) -> a -> c
.) ((Benchmark -> Ap ((->) [String]) TestOutput)
-> Benchmark -> TestOutput)
-> (OptionSet -> Benchmark -> Ap ((->) [String]) TestOutput)
-> OptionSet
-> Benchmark
-> TestOutput
forall b c a. (b -> c) -> (a -> b) -> a -> c
. TreeFold (Ap ((->) [String]) TestOutput)
-> OptionSet -> Benchmark -> Ap ((->) [String]) TestOutput
forall b. Monoid b => TreeFold b -> OptionSet -> Benchmark -> b
foldTestTree
TreeFold (Ap ((->) [String]) TestOutput)
forall b. Monoid b => TreeFold b
trivialFold { foldSingle :: forall t.
IsTest t =>
OptionSet -> String -> t -> Ap ((->) [String]) TestOutput
foldSingle = (String -> t -> Ap ((->) [String]) TestOutput)
-> OptionSet -> String -> t -> Ap ((->) [String]) TestOutput
forall a b. a -> b -> a
const String -> t -> Ap ((->) [String]) TestOutput
forall b. String -> b -> Ap ((->) [String]) TestOutput
runSingleTest, foldGroup :: OptionSet
-> String
-> Ap ((->) [String]) TestOutput
-> Ap ((->) [String]) TestOutput
foldGroup =
#if MIN_VERSION_tasty(1,4,0)
(String
-> Ap ((->) [String]) TestOutput -> Ap ((->) [String]) TestOutput)
-> OptionSet
-> String
-> Ap ((->) [String]) TestOutput
-> Ap ((->) [String]) TestOutput
forall a b. a -> b -> a
const String
-> Ap ((->) [String]) TestOutput -> Ap ((->) [String]) TestOutput
forall a a. a -> Ap ((->) [a]) a -> Ap ((->) [a]) a
runGroup
#else
runGroup
#endif
}
where
runSingleTest :: String -> b -> Ap ((->) [String]) TestOutput
runSingleTest String
name = Ap ((->) [String]) TestOutput -> b -> Ap ((->) [String]) TestOutput
forall a b. a -> b -> a
const (Ap ((->) [String]) TestOutput
-> b -> Ap ((->) [String]) TestOutput)
-> Ap ((->) [String]) TestOutput
-> b
-> Ap ((->) [String]) TestOutput
forall a b. (a -> b) -> a -> b
$ ([String] -> TestOutput) -> Ap ((->) [String]) TestOutput
forall (f :: * -> *) a. f a -> Ap f a
Ap (([String] -> TestOutput) -> Ap ((->) [String]) TestOutput)
-> ([String] -> TestOutput) -> Ap ((->) [String]) TestOutput
forall a b. (a -> b) -> a -> b
$ \[String]
prefix -> String -> IO () -> (Result -> IO ()) -> TestOutput
PrintTest String
name
(Handle -> String -> IO ()
hPutStr Handle
h (String -> IO ()) -> String -> IO ()
forall a b. (a -> b) -> a -> b
$ ShowS
encodeCsv (String -> [String] -> String
forall a. [a] -> [[a]] -> [a]
intercalate String
"." ([String] -> [String]
forall a. [a] -> [a]
reverse (String
name String -> [String] -> [String]
forall a. a -> [a] -> [a]
: [String]
prefix))) String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
",")
(Handle -> String -> IO ()
hPutStrLn Handle
h (String -> IO ()) -> (Result -> IO String) -> Result -> IO ()
forall (m :: * -> *) b c a.
Monad m =>
(b -> m c) -> (a -> m b) -> a -> m c
<=< String -> IO String
formatMessage (String -> IO String) -> (Result -> String) -> Result -> IO String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Result -> String
resultDescription)
runGroup :: a -> Ap ((->) [a]) a -> Ap ((->) [a]) a
runGroup a
name (Ap [a] -> a
grp) = ([a] -> a) -> Ap ((->) [a]) a
forall (f :: * -> *) a. f a -> Ap f a
Ap (([a] -> a) -> Ap ((->) [a]) a) -> ([a] -> a) -> Ap ((->) [a]) a
forall a b. (a -> b) -> a -> b
$ \[a]
prefix -> [a] -> a
grp (a
name a -> [a] -> [a]
forall a. a -> [a] -> [a]
: [a]
prefix)
csvOutput :: TestOutput -> StatusMap -> IO ()
csvOutput :: TestOutput -> StatusMap -> IO ()
csvOutput = (Traversal IO -> IO ()
forall (f :: * -> *). Traversal f -> f ()
getTraversal (Traversal IO -> IO ())
-> (StatusMap -> Traversal IO) -> StatusMap -> IO ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
.) ((StatusMap -> Traversal IO) -> StatusMap -> IO ())
-> (TestOutput -> StatusMap -> Traversal IO)
-> TestOutput
-> StatusMap
-> IO ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (String -> IO () -> IO Result -> (Result -> IO ()) -> Traversal IO)
-> (String -> IO () -> Traversal IO -> Traversal IO)
-> TestOutput
-> StatusMap
-> Traversal IO
forall b.
Monoid b =>
(String -> IO () -> IO Result -> (Result -> IO ()) -> b)
-> (String -> IO () -> b -> b) -> TestOutput -> StatusMap -> b
foldTestOutput ((IO () -> IO Result -> (Result -> IO ()) -> Traversal IO)
-> String
-> IO ()
-> IO Result
-> (Result -> IO ())
-> Traversal IO
forall a b. a -> b -> a
const IO () -> IO Result -> (Result -> IO ()) -> Traversal IO
forall (f :: * -> *) a a.
Monad f =>
f a -> f a -> (a -> f ()) -> Traversal f
foldTest) ((IO () -> Traversal IO -> Traversal IO)
-> String -> IO () -> Traversal IO -> Traversal IO
forall a b. a -> b -> a
const ((Traversal IO -> Traversal IO)
-> IO () -> Traversal IO -> Traversal IO
forall a b. a -> b -> a
const Traversal IO -> Traversal IO
forall a. a -> a
id))
where
foldTest :: f a -> f a -> (a -> f ()) -> Traversal f
foldTest f a
printName f a
getResult a -> f ()
printResult =
f () -> Traversal f
forall (f :: * -> *). f () -> Traversal f
Traversal (f () -> Traversal f) -> f () -> Traversal f
forall a b. (a -> b) -> a -> b
$ f a
printName f a -> f a -> f a
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> f a
getResult f a -> (a -> f ()) -> f ()
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= a -> f ()
printResult
encodeCsv :: String -> String
encodeCsv :: ShowS
encodeCsv String
xs
| (Char -> Bool) -> String -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any (Char -> String -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` String
xs) String
",\"\n\r"
= Char
'"' Char -> ShowS
forall a. a -> [a] -> [a]
: (Char -> String) -> ShowS
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap (\Char
x -> if Char
x Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== Char
'"' then String
"\"\"" else [Char
x]) String
xs String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
"\""
| Bool
otherwise = String
xs