Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Haskell calculate time of function performing

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
like image 973
yck Avatar asked Apr 17 '13 03:04

yck


3 Answers

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.

like image 69
Philip JF Avatar answered Sep 30 '22 18:09

Philip JF


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.

like image 34
Matt S Avatar answered Sep 30 '22 16:09

Matt S


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.

like image 35
John L Avatar answered Sep 30 '22 17:09

John L