i tried to code to calculate time that a function costs
list <- buildlist 10000 10000
starttime <- getClockTime
let sortedlist = quicksort list
endtime <- getClockTime
let difftime = diffClockTimes endtime starttime
function buildlist:
buildlist :: Int -> Int -> IO [Int]
buildlist n m = do
seed <- getStdGen
let l = randomRs (0, m) seed
let list = take n l
return list
function quicksort:
quicksort [] = []
quicksort (x:xs) =
let head = [a|a<-xs,a<=x]
tail = [a|a<-xs,a>x]
in quicksort head ++ [x] ++ quicksort tail
first question: when i output the difftime, it is always zero no matter how long the list is.
second one: i wonder what ">>=" in the code from Real World Haskell means.
getClockTime >>= (\(TOD sec _) -> return sec)
third: i write this to get tdSec and tdPicosec from a TimeDiff variable.
is there any easier way?
time <-(\(TimeDiff _ _ _ _ _ s ps) -> return [ ( \a -> fromIntegral a :: Double ) s , ( \a -> fromIntegral a :: Double ) ps ] ) difftime
Question 1:
Your code does not sort the list! It simply defines the name sortedlist
as being quicksort list
but this wont be computed until the value is actually needed. That is lazy evaluation. We do not do extra work in this language.
Since benchmarks are extra useless work (that is the point), this makes benchmarking hard.
Your choices
- Use
seq
. seq
has type a -> b -> b
and has the behavior of evaluating its first argument to what is called "weak head normal form". Here since you want to force the entire list you might want to use deepseq
- Use a proper benchmarking package like criterion (prefered and easier)
Question 2:
>>=
is the monadic bind operator. Here it takes an IO
action of type IO a
and a function a -> IO b
and combines them together to make a new action of type IO b
. This does the same thing as do notation. foo >>= \x -> expr
is the same thing as
do x <- foo
expr
and in fact, do
notation is just syntactic sugar for >>=
I'm not sure what is being asked in question 3--perhaps it should get its own Stackoverflow question.
difftime
is always zero, because Haskell's lazy evaluation order has completely optimized away the actual sort. Nothing in your program accesses sortedlist
, so it's never even computed.
As stated in another answer, you can force your program to compute sortedlist
with a somewhat magical function called deepseq
from Control.Deepseq
. deepseq v
is equivalent to id
, except that it has the side-effect of forcing v
to be fully evaluated.
starttime <- getClockTime
let sortedlist = quicksort list
endtime <- deepseq sortedlist getClockTime
As for your second question, yes, there's an easier way to access the fields of a TimeDiff
. The field names in Data
declaration second as getter functions. So, tdSec td
gets the seconds of td
and tdPicosec td
gets the picoseconds.
For measuring the evaluation time of pure functions, you might be interested in my Chronograph
package: http://hackage.haskell.org/package/chronograph
Here's a short example of how to use it:
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
A few points:
- I evaluate the sum of the original
theList
to force its construction and reversal. If it's not forced here, the cost of construction will be attributed to the evaluation of the expression passed into chronoNF
chronoNF
evaluates using the same strategy as deepseq
as some other answers discuss. chronograph provides other functions for different evaluation strategies. For example, we could evaluate to weak head normal form, which wouldn't actually fully sort the list.
chronograph can also measure IO
expressions, but those are typically much simpler to deal with than non-IO expressions.