[英]How can one force evaluation of the same value multiple times in Haskell?
I have a function bench
which can be used to calculate the time taken to evaluate an action
:我有一个函数
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
I'm trying to take a mean of multiple benchmarks of said action
, however subsequent evaluations of action
happen almost instantly as it has already been evaluated once:我试图对所述
action
的多个基准进行平均,但是随后的action
评估几乎立即发生,因为它已经评估过一次:
benchN :: Int -> a -> IO Benchmark
benchN count action
= BenchmarkN <$> (mapM bench $ replicate count action)
Is there anyway to force action
to be evaluated multiple times, such that it will take the full time to evaluate?无论如何,是否要强制多次评估
action
,从而需要花费全部时间来评估?
Link to repo: https://github.com/wdhg/benchy回购链接: https : //github.com/wdhg/benchy
The technique that criterion
uses is to compile a function whnf'
in its own module with no inlining and a special -fno-full-laziness
optimization flag, something like: 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' #-}
Here, the computation is represented in two parts -- as a function and an argument to call it on.在这里,计算由两部分表示——作为一个函数和一个调用它的参数。 The function
whnf'
turns it into a benchmarking function Int -> IO ()
that takes a replication count and will safely re-run the computation (specifically, by forcing it to weak head normal form) the given number of times.函数
whnf'
将其转换为基准测试函数Int -> IO ()
,该函数获取复制计数,并将安全地重新运行计算(特别是,通过强制计算为弱头范式)给定的次数。
Note that the replication count here is not for generating a bunch of separate timings.请注意,这里的复制计数不是用于生成一堆单独的计时。 Rather, it's used to scale up the time when benchmarking really fast computations so that timing overhead doesn't swamp the benchmark.
相反,它用于在对真正快速计算进行基准测试时扩展时间,以便计时开销不会淹没基准测试。 For slow computations, you can use a count of 1.
对于较慢的计算,您可以使用计数 1。
In your main benchmarking module, you'll generally also need to represent the expression to be benchmarked using the same two parts, a function and an argument to call it on.在您的主要基准测试模块中,您通常还需要使用相同的两部分来表示要进行基准测试的表达式,一个函数和一个调用它的参数。 Though not necessary, it may be convenient to introduce a data type for this, including the replication count scale:
虽然不是必需的,但为此引入一种数据类型可能会很方便,包括复制计数规模:
data Benchmarkable a b = Benchmarkable (a -> b) a Int
and then you can bench it a single time with:然后你可以用一次:
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
or multiple times with:或多次使用:
benchN :: Int -> Benchmarkable a b -> IO Benchmark
benchN count b = BenchmarkN <$> replicateM count (bench b)
If you have a slow Fibonacci implemenation:如果您有一个缓慢的斐波那契实现:
slowFib :: Integer -> Integer
slowFib 0 = 0
slowFib 1 = 1
slowFib n = slowFib (n-1) + slowFib (n-2)
where slowFib 35
takes an appreciable fraction of a second to run, you can can try:在这里,
slowFib 35
需要很slowFib 35
时间才能运行,您可以尝试:
main = print =<< benchN 10 (Benchmarkable slowFib 35 1)
and it seems to work okay, outputting:它似乎工作正常,输出:
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]
The full code for the WHNF module: 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' #-}
and the benchmark itself in a separate module:和基准本身在一个单独的模块中:
-- 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.