
On 26/01/07, Sean McLaughlin
Hello,
I'm trying to write a simple function to time an application.
-- this doesn't work
time f x = do n1 <- CPUTime.getCPUTime let res = f x in do n2 <- CPUTime.getCPUTime return (res,n2 - n1)
On a function that takes 8 seconds to complete, returns (True,46000000)
According to the documentation, this time is in picoseconds, making this 46 microseconds.
The statement "let res = f x" on its own causes essentially no computation to occur. In your program, res is only getting computed when the result of that do-block is finally *printed*. Before that, it's just a pointer to some code which has yet to run. There's no reason to compute res there, and so it isn't computed. Note that the amount of time spent computing res might depend on just how much of it is ever used by the program. Consider the case where res is an infinite list, for example. One easy solution would be to print the value of res between the timers, though that will include the time for show and IO of course. Another is to use Control.Exception.evaluate to force the evaluation of res to occur in sequence with the other IO actions. Be aware that if res is compound data, you may need to sequence the computation of each of the components of res as well, as Control.Exception.evaluate will only cause evaluation to occur to the point of determining the top-level constructor (Weak Head Normal Form). In order to control evaluation, there is a primitive in Haskell called seq with the behaviour that evaluating seq x y will cause x to become evaluated to WHNF before resulting in y. Note that this only occurs when the seq itself is evaluated. Writing seq x x instead of x is redundant, as evaluation is being forced anyway, by the time the seq is seen. Using this, one can write functions which traverse datastructures and cause them to be fully evaluated as soon as any part of them is needed. strictPair (x,y) = x `seq` y `seq` (x,y) strictList xs = foldr seq xs xs As a generalisation of this idea, in the module Control.Parallel.Strategies, there is a class called NFData, with the function rnf :: (NFData a) => a -> () (note that the type doesn't really capture the evaluation side-effect -- when the empty tuple returned is evaluated, the whole structure passed to rnf will be forced.). Using this, we could write: strictList xs = rnf xs `seq` xs Anyway, that's the quick intro to subverting Haskell's lazy evaluation. Before I go, there's another rather more honest way to do this sort of thing, provided that you're more interested in the timing for your own sake rather than having it be a value that your program is interested in. GHC has a fairly extensive profiler which will produce beautifully detailed reports on just how much time and memory were spent on each part of your program, and it will even let you tag subexpressions to watch and so on. For that, read http://www.haskell.org/ghc/docs/latest/html/users_guide/profiling.html Or if you're impatient, just build your program with the compiler options -prof -auto-all, and then run the resulting executable with +RTS -p -RTS on the commandline. It'll produce a profiling report which will show the relative amount of time spent in each part of the program, or +RTS -P -RTS, which will report the number of ticks and actual memory used in addition to the percentages. Hope this helps, - Cale