[英]How can one force evaluation of the same value multiple times in Haskell?
我有一个函数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
,从而需要花费全部时间来评估?
回购链接: https : //github.com/wdhg/benchy
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
需要很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)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.