Haskell计算函数执行的时间

时间:2013-04-17 03:25:11

标签: haskell benchmarking

我尝试编码以计算函数成本的时间

list <- buildlist 10000 10000    
starttime <- getClockTime
let sortedlist = quicksort list
endtime <- getClockTime
let difftime = diffClockTimes endtime starttime

功能构建列表:

buildlist :: Int -> Int -> IO [Int]
buildlist n m = do
    seed <- getStdGen
    let l = randomRs (0, m) seed
    let list = take n l
    return list

功能快速排序:

quicksort [] = []
quicksort (x:xs) =
    let head = [a|a<-xs,a<=x]
        tail = [a|a<-xs,a>x]
    in quicksort head ++ [x] ++ quicksort tail

第一个问题:当我输出difftime时,无论列表有多长,它总是为零。

第二个:我想知道Real World Haskell代码中的“&gt;&gt; =”是什么意思。

getClockTime >>= (\(TOD sec _) -> return sec)

第三:我这是为了从 TimeDiff 变量中获取 tdSec tdPicosec 。 有没有更简单的方法?

time <-(\(TimeDiff _ _ _ _ _ s ps) -> return [ ( \a -> fromIntegral a :: Double ) s , ( \a -> fromIntegral a :: Double ) ps ] ) difftime

3 个答案:

答案 0 :(得分:11)

问题1:

您的代码不会对列表进行排序!它只是将名称sortedlist定义为quicksort list,但在实际需要该值之前不会计算。那是懒惰的评价。我们不会用这种语言做额外的工作。

由于基准是额外无用的工作(这就是重点),这使基准测试变得困难。

您的选择

  • 使用seqseq具有类型a -> b -> b,并且具有评估其第一个参数的行为,即所谓的“弱头正常形式”。在这里,由于您要强制使用整个列表,因此您可能需要使用deepseq
  • 使用适当的基准测试包,例如criterion(首选且更容易)

问题2:

>>=是monadic绑定运算符。此处需要IO类型的IO a操作和函数a -> IO b,并将它们组合在一起以生成类型为IO b的新操作。这与表示法的作用相同。 foo >>= \x -> expr

相同
 do x <- foo
    expr

事实上,do符号只是>>=

的语法糖

我不确定问题3中的问题是什么 - 也许它应该得到自己的Stackoverflow问题。

答案 1 :(得分:4)

difftime始终为零,因为Haskell的惰性评估顺序已经完全优化了实际排序。程序中的任何内容都不会访问sortedlist,因此它甚至都不会被计算出来。

如另一个答案中所述,您可以强制您的程序使用来自sortedlist的名为deepseq的神奇函数来计算Control.Deepseqdeepseq v相当于id,但它具有强制v被完全评估的副作用。

starttime <- getClockTime
let sortedlist = quicksort list
endtime <- deepseq sortedlist getClockTime

至于你的第二个问题,是的,有一种更简单的方式来访问TimeDiff的字段。 Data声明中的字段名称为getter函数。因此,tdSec td获得td秒,tdPicosec td获得皮秒。

答案 2 :(得分:3)

为了衡量纯函数的评估时间,您可能会对我的Chronograph包感兴趣:http://hackage.haskell.org/package/chronograph

以下是如何使用它的简短示例:

Prelude Data.Chronograph> :m Data.Chronograph Data.List
Prelude Data.Chronograph Data.List> let theList = reverse [1..1000]
Prelude Data.Chronograph Data.List> sum theList 
500500
Prelude Data.Chronograph Data.List> let timed = chronoNF (sort theList)
Prelude Data.Chronograph Data.List> measure timed
0.000062s
Prelude Data.Chronograph Data.List> head $ val timed
1

几点:

  • 我评估原始theList的总和,以强制其构造和逆转。如果它不是强制在这里,那么构建成本将归因于对chronoNF
  • 中传递的表达式的评估
  • chronoNF使用与deepseq相同的策略进行评估,正如其他一些答案所讨论的那样。计时码表为不同的评估策略提供其他功能。例如,我们可以评估弱头正常形式,这实际上不会对列表进行完全排序。

计时码表还可以测量IO个表达式,但这些表达式通常比非IO表达式更容易处理。