如何在Haskell中多次评估相同值的力?

问题描述 投票:4回答:1

我有一个函数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

haskell benchmarking lazy-evaluation
1个回答
0
投票

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)

© www.soinside.com 2019 - 2024. All rights reserved.