88

Is there a simple method to compute time of function execution in Haskell?

Kara
  • 6,115
  • 16
  • 50
  • 57
0xAX
  • 20,957
  • 26
  • 117
  • 206
  • the answers to my question about criterion may contain some helpful usage examples http://stackoverflow.com/questions/6637968/how-to-use-criterion-to-measure-performance-of-haskell-programs . – gatoatigrado Jul 20 '11 at 22:14
  • 3
    Also, this is a somewhat nuanced situation, because functions don't have to be fully "executed" in Haskell. They just have to be expanded enough for whatever required value. Consider `head [1..]`, which takes the first element of an infinite list. – gatoatigrado Jul 20 '11 at 22:16
  • @gatoatigrado Thats why criterion has the `whnf` and `nf` functions. – alternative Jul 20 '11 at 22:35

6 Answers6

129

Simplest things is to just do :set +s in ghci, and then you can see the execution time of anything you run, along with memory usage.

Willem Van Onsem
  • 443,496
  • 30
  • 428
  • 555
Rafal
  • 1,330
  • 1
  • 8
  • 2
  • 25
    Functions run much slower in ghci, however. In my test, about 10 times slower. – Ray Jun 14 '13 at 09:52
  • 3
    Is it possible to set the precision of the measured time, like in milliseconds? – SiXoS May 28 '16 at 19:19
  • 1
    Slower, yes, but this appears to be very valuable for demonstrating differences in time and space consumption between algorithms. For example
    
    slow_fib :: Int -> Integer
    slow_fib 0 = 0
    slow_fib 1 = 1
    slow_fib n = slow_fib (n-2) + slow_fib (n-1)
    
    -- vs.
    
    memoized_fib :: Int -> Integer
    memoized_fib = (map fib [0 ..] !!)
       where fib 0 = 0
             fib 1 = 1
             fib n = memoized_fib (n-2) + memoized_fib (n-1)
    
    Where you can see the first function not only take up a LOT more time, but also several orders of magnitude more space.
    – Alex Hart Aug 16 '18 at 11:56
30

The criterion package was made specifically to do this well.

Daniel Wagner
  • 145,880
  • 9
  • 220
  • 380
11

See if http://hackage.haskell.org/package/timeit suits your needs.

Tyler
  • 21,762
  • 11
  • 61
  • 90
7

function execution time benchmark is included in Criterion.Measurement

for example, if I want to capture the time of someIOFunction :: IO ()

import Criterion.Measurement
main = secs <$> time_ someIOFunction >>= print
Zane XY
  • 2,743
  • 1
  • 22
  • 12
3

Criterion is the most sophisticated method, although I found it difficult to start, and it seems targeted to benchmarking programs. I wanted to compute the time of execution and use that data within my program and it doesn't seem to address this need, at least it's not immediately apparent.

TimeIt is very simple and does what I wanted, except it does not handle pure functions well. The time returned for a pure function is the thunk allocation time (AFAIK) and even with using seq it can be difficult to get what you want.

What is working for me is based on TimeIt.

import System.TimeIt

timeItTPure :: (a -> ()) -> a -> IO (Double,a)
timeItTPure p a = timeItT $ p a `seq` return a

In timeItTPure p a, p is the function responsible for evaluating the result of a pure calculation, a, as deeply as needed to get the good evaluation timing. Maybe this is a simple pattern match, maybe it's counting the length of a list, maybe its seq every element in the list, maybe its a deepseq, etc.

The use of seq is tricky. Note, the below function does not perform as desired. Haskell is a mysterious thing.

badTimeItTPure a = timeItT . return $ seq (p a) a
trevor cook
  • 1,531
  • 8
  • 22
  • I'm not sure how to apply `timeItTPure`. How would I measure the execution time of a function `f :: a -> a` applied to an argument `x :: a`? `timeItTPure f x` results in a type error, as `f` doesn't return `()`. – erictapen Aug 31 '21 at 22:02
  • 1
    @erictapen you could try ``timeItT $ f x `seq` return x`` or ``timeItT $ let y = f x in y `seq` return y`` . – Will Ness Nov 05 '21 at 14:21
1

https://github.com/chrissound/FuckItTimer

start' <- start
timerc start' "begin"
print "hello"
timerc start' "after printing hello"
benchmark
timerc start' "end"
end <- getVals start'
forM_ (timert end) putStrLn

Outputs:

"hello"
begin -> after printing hello: 0.000039555s
after printing hello -> end: 1.333936928s

This seems to work fine for my very simple usecase.

dfeuer
  • 48,079
  • 5
  • 63
  • 167
Chris Stryczynski
  • 30,145
  • 48
  • 175
  • 286
  • Hmmmm.... Tried editing to get the highlighting right, but clearly it doesn't like me. Sorry. – dfeuer Oct 27 '20 at 19:05