tags:

views:

159

answers:

5

Hi

I have:

main :: IO ()
main = do
     iniciofibonaccimap <- getCPUTime
     let fibonaccimap = map fib listaVintesete
     fimfibonaccimap <- getCPUTime
     let difffibonaccimap = (fromIntegral (fimfibonaccimap - iniciofibonaccimap)) / (10^12)
     printf "Computation time fibonaccimap: %0.3f sec\n" (difffibonaccimap :: Double)

listaVintesete :: [Integer]
listaVintesete = replicate 100 27

fib :: Integer -> Integer
fib 0 = 0
fib 1 = 1
fib n = fib (n-1) + fib (n-2)

But

*Main> main
Computation time fibonaccimap: 0.000 sec

I do not understand why this happens. Help-me thanks.

A: 

10^12 is an integer, which forces the value of fromIntegral to be an integer, which means difffibonaccimap is assigned a rounded value, so it's 0 if the time is less than half a second. (That's my guess, anyway. I don't have time to look into it.)

Adam Crume
This can't be true, though, because `(/)` has type `Fractional a => a -> a -> a`; and in fact, since he forces `difffibonaccimap` to be a `Double` on the next line, `10^12` must be a double.
Antal S-Z
Adam simply misunderstood the type of `(^)`, which is ` (Num a, Integral b) => a -> b -> a`. The result being in the `Num a` class makes Double possible. It isn't an unreasonable misunderstanding as `(**) :: Floating a => a -> a -> a` leaves many people incorrectly thinking of `(^)` as the integral version of `(**)`.
TomMD
@TomMD: You got me. I was assuming `(^)` was `Integral a => a -> a -> a` or something similar.
Adam Crume
+7  A: 

Haskell is lazy. The computation you request in the line

let fibonaccimap = map fib listaVintesete

doesn't actually happen until you somehow use the value of fibonaccimap. Thus to measure the time used, you'll need to introduce something that will force the program to perform the actual computation.

ETA: I originally suggested printing the last element to force evaluation. As TomMD points out, this is nowhere near good enough -- I strongly recommend reading his response here for an actually working way to deal with this particular piece of code.

Mikael Vejdemo-Johansson
Thanks, that's right, lazy evaluation.
Gmp
This answer is incorrect. Printing the last element in a list does not force the evaluation of any of the rest of the list. Please see my answer and comments.
TomMD
@TomMD You're right, of course. My bad.
Mikael Vejdemo-Johansson
+4  A: 

I suspect you are a "victim" of lazy evaluation. Nothing forces the evaluation of fibonaccimap between the timing calls, so it's not computed.

Edit I suspect you're trying to benchmark your code, and in that case it should be pointed out that there are better ways to do this more reliably.

gspr
No, do not do the hack-job of printing the last element in the list. This does not force evaluation of any other elements in the list.
TomMD
TomMD: Thanks! Fixed.
gspr
+5  A: 

As others have said, this is due to lazy evaluation. To force evaluation you should use the deepseq package and BangPatterns:

{-# LANGUAGE BangPatterns #-}
import Control.DeepSeq
import Text.Printf
import System.CPUTime

main :: IO ()
main = do
 iniciofibonaccimap <- getCPUTime
 let !fibonaccimap = rnf $ map fib listaVintesete
 fimfibonaccimap <- getCPUTime
 let difffibonaccimap = (fromIntegral (fimfibonaccimap - iniciofibonaccimap)) / (10^12)
 printf "Computation time fibonaccimap: %0.3f sec\n" (difffibonaccimap :: Double)
...

In the above code you should notice three things:

  1. It compiles (modulo the ... of functions you defined above). When you post code for questions please make sure it runs (iow, you should include imports)
  2. The use of rnf from deepseq. This forces the evaluation of each element in the list.
  3. The bang pattern on !fibonaccimap, meaning "do this now, don't wait". This forces the list to be evaluated to weak-head normal form (whnf, basically just the first constructor (:)). Without this the rnf function would itself remain unevaluated.

Resulting in:

$ ghc --make ds.hs
$ ./ds
Computation time fibonaccimap: 6.603 sec

If you're intending to do benchmarking you should also use optimization (-O2) and the Criterion package instead of getCPUTime.

TomMD
Thanks for the help, I did not know the Criterion, I tried to install but could not, I'll try to figure out what is happening. Thanks
Gmp
@user Great, do use Criterion. Beware of these entirely incorrect "quick-and-dirty" suggestions using `print` and/or `show`. Printing the last element in this list wouldn't force computation of any of the other elements! In my example (without optimization) that changes the result from 6.6 seconds to 0.403 seconds.
TomMD
A: 

Lazy evaluation has in fact bitten you, as the other answers have said. Specifically, 'let' doesn't force the evaluation of an expression, it just scopes a variable. The computation won't actually happen until its value is demanded by something, which probably won't happen until an actual IO action needs its value. So you need to put your print statement between your getCPUTime evaluations. Of course, this will also get the CPU time used by print in there, but most of print's time is waiting on IO. (Terminals are slow.)

Edward Amsden
He never prints his result, the print is of the elapsed time. Also, printing the result is a really hack-job way of forcing evaluation, it's better for people to learn the parallel and deepseq packages.
TomMD
Thanks everyone, good to know that many people said to me, I have many questions and did not know whom to ask. The response of TomMD is correct, I tested worked, I'm curious to know the Criterion, but I could not install. For now I'll finish the other steps of the program, and later come back to try the Criterion, it looks quite interesting for parallel programming.I'm doing this program to compare with other Erlang described in the book by Joe Armstrong.Certainly I have more questions ...Thanks
Gmp
@TomMD oops missed that.
Edward Amsden