我有一个函数bench
,可用于计算评估action
所需的时间:
data Benchmark
= Benchmark POSIXTime POSIXTime
| BenchmarkN [Benchmark]
bench :: a -> IO Benchmark
bench action
= do
start <- getPOSIXTime
let !_ = action
end <- getPOSIXTime
return $ Benchmark start end
[我正在尝试对action
进行多个基准测试,但是action
的后续评估几乎已经发生,因为它已经被评估过一次:
benchN :: Int -> a -> IO Benchmark
benchN count action
= BenchmarkN <$> (mapM bench $ replicate count action)
反正是否有强迫action
进行多次评估,这样将需要全部时间进行评估的问题?
criterion
使用的技术是在没有内联和特殊whnf'
优化标志的情况下,在自己的模块中编译函数-fno-full-laziness
,例如:
-- WHNF.hs
{-# OPTIONS_GHC -fno-full-laziness #-}
module WHNF (whnf') where
whnf' :: (a -> b) -> a -> (Int -> IO ())
whnf' f x = go
where
go n | n <= 0 = return ()
| otherwise = f x `seq` go (n-1)
{-# NOINLINE whnf' #-}
这里,计算分为两部分-作为函数和调用它的参数。函数whnf'
将其转换为基准测试函数Int -> IO ()
,该函数将进行复制计数,并将在给定的次数内安全地重新运行计算(具体而言,通过将其强制为弱磁头范式)。
请注意,此处的复制计数不是用于生成一堆单独的时序。而是,它用于扩展对真正快速的计算进行基准测试的时间,以便计时开销不会淹没基准测试。对于慢速计算,可以使用1。
在您的主要基准测试模块中,通常<>需要使用相同的两个部分(要调用的函数和参数)来表示要进行基准测试的表达式。尽管不是必需的,但为此引入一个数据类型可能很方便,包括复制计数范围:data Benchmarkable a b = Benchmarkable (a -> b) a Int
然后您可以使用:
data Benchmark = Benchmark POSIXTime POSIXTime | BenchmarkN [Benchmark] deriving (Show) bench :: Benchmarkable a b -> IO Benchmark bench (Benchmarkable f a n) = do start <- getPOSIXTime () <- whnf' f a n end <- getPOSIXTime return $ Benchmark start end
或多次使用:
benchN :: Int -> Benchmarkable a b -> IO Benchmark benchN count b = BenchmarkN <$> replicateM count (bench b)
如果您的斐波那契实施缓慢:
slowFib :: Integer -> Integer slowFib 0 = 0 slowFib 1 = 1 slowFib n = slowFib (n-1) + slowFib (n-2)
slowFib 35
需要花费一秒钟的时间才能运行,您可以尝试:
main = print =<< benchN 10 (Benchmarkable slowFib 35 1)
并且似乎可以正常工作,输出:
BenchmarkN [Benchmark 1586018307.738716168s 1586018308.179642319s, Benchmark 1586018308.179642466s 1586018308.618854568s, Benchmark 1586018308.618854653s 1586018309.057612242s, Benchmark 1586018309.057612287s 1586018309.496228626s, Benchmark 1586018309.496228714s 1586018309.934910649s, Benchmark 1586018309.934910697s 1586018310.373258208s, Benchmark 1586018310.373258295s 1586018310.811727495s, Benchmark 1586018310.811727542s 1586018311.250130875s, Benchmark 1586018311.250131005s 1586018311.689046116s, Benchmark 1586018311.689046207s 1586018312.127901112s]
WHNF模块的完整代码:
-- WHNF.hs {-# OPTIONS_GHC -fno-full-laziness #-} module WHNF (whnf') where whnf' :: (a -> b) -> a -> (Int -> IO ()) whnf' f x = go where go n | n <= 0 = return () | otherwise = f x `seq` go (n-1) {-# NOINLINE whnf' #-}
和基准本身在单独的模块中:
-- Benchmark.hs {-# OPTIONS_GHC -O2 #-} import WHNF import Data.Time.Clock.POSIX import Control.Monad data Benchmarkable a b = Benchmarkable (a -> b) a Int data Benchmark = Benchmark POSIXTime POSIXTime | BenchmarkN [Benchmark] deriving (Show) bench :: Benchmarkable a b -> IO Benchmark bench (Benchmarkable f a n) = do start <- getPOSIXTime () <- whnf' f a n end <- getPOSIXTime return $ Benchmark start end benchN :: Int -> Benchmarkable a b -> IO Benchmark benchN count b = BenchmarkN <$> replicateM count (bench b) slowFib :: Integer -> Integer slowFib 0 = 0 slowFib 1 = 1 slowFib n = slowFib (n-1) + slowFib (n-2) main :: IO () main = print =<< benchN 10 (Benchmarkable slowFib 35 1)