What functions are cached in Haskell? - haskell

I have the following code:
memoize f = (map f [0 ..] !!)
fib' 0 = 1
fib' 1 = 1
fib' n = fib' (n - 1) + fib' (n - 2)
fibMemo n = memoize fib' n
fibMemo' = memoize fib'
(I am aware of that fibonacci implementation has exponential time complexity and does not use the cache)
The first time I execute fibmemo' 30 it takes 3 seconds, and the second time it takes ~0 seconds, because the result is cached. But the first version, fibmemo, does not get the result cached, it always takes 3 seconds to execute. The only difference is the definition (which as far as I know are equivalent).
So my question is, what functions are cached in Haskell?
I have already read https://wiki.haskell.org/Memoization and does not resolve my question.

Essentially, the functions you defined behave as the following ones:
fibMemo n = let m = map fib' [0..] in m !! n
fibMemo' = let m = map fib' [0..] in (m !!)
Why is fibMmemo' more efficient? Well, we can rewrite it as
fibMemo' = let m = map fib' [0..] in \n -> m !! n
which makes it more clear that the single list m gets created before n is taken as input. This means that all the calls to fibMemo' will use the same m. The first call evaluates a part of m slowly, and the successive calls will reuse that cached result (assuming the call hits the cache, of course, otherwise another part of m is evaluated and cached).
Instead, fibMemo is equivalent to
fibMemo = \n -> let m = map fib' [0..] in m !! n
which takes the input n before the list m gets created. So, each call gets a new cache, which is pointless, since the whole purpose of a cache is that it is reused later.
The order of the lambda \n -> vs the let m = .. matters a lot in terms of the performance. Since m = .. does not use n, technically the let m = .. can be floated outwards, essentially turning fibMemo into fibMemo', without affecting the semantics. However, as you discovered, this does not preserve performance, in general!
This is indeed an optimization that GHC could perform, but does not, because it can easily make the performance significantly worse.

Related

How does haskell manage memory of recursive function calls

I have been working on a problem that benefits a lot from catching results of my functions and in my research I came across this article. I am stunned at how simple is the core in "Memoization with recursion" section namely:
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)
I feel like I understand how it work but do correct me if I'm wrong - this function saves a list which is populated using same function.
What bothers me is that I don't understand why this works, originally I was under impression that once haskell evaluates a function it releases memory that was used to store variables inside this function, but here it seems that if part of the list was evaluated by one call of this function those values are still available to another call of the same function.
Just typing this up makes my head hurt, because I don't understand why value used in calculation of fib 2 should be available in calculation of fib 3 or better yest fib 100?
My gut feeling tells me that this behavior has two problems(I'm probably wrong but again not sure why):
purity of this function we are evaluating one call using variable that did not arrive from parameters of this function
memory leaks no longer sure when will haskell release memory from this list
I think it's easier to understand if you compare your definition to this:
not_memoized_fib :: Int -> Integer
not_memoized_fib m = map fib [0 ..] !! m
where fib 0 = 0
fib 1 = 1
fib n = not_memoized_fib (n-2) + not_memoized_fib (n-1)
The definition above is essentially the same as yours, except that it takes an explicit argument m. It is a so-called eta-expansion of the previous function, and is semantically equivalent to it. Yet, operationally, this has drastically worse performance, since memoization here does not take place.
Why? Well, your function defines the list map fib [0..] before taking the (implicit) input parameter m, so there is only one list around, for all m we may pass later on as arguments. Instead, in not_memoized_fib we first take m as input, and then define the list, making the function create a list for every call to not_memoized_fib, destroying performance.
It is even easier to see if we use let and lambdas instead of where. Compare
memoized :: Int -> Integer
memoized = let
list = map fib [0..]
fib 0 = 0
fib 1 = 1
fib n = memoized (n-1) + memoized (n-2)
in \m -> list !! m
-- ^^ here we take m, after everything is defined,
with its let over lambda (*) code structure, to
not_memoized :: Int -> Integer
not_memoized = \m -> let
-- ^^ here we take m, before everything is defined, so
-- we define local bindings {list,fib} at every call
list = map fib [0..]
fib 0 = 0
fib 1 = 1
fib n = not_memoized (n-1) + not_memoized (n-2)
in list !! m
with the let inside the lambda.
In the former case, there is only one list around, while in the latter there is one list for each call.
(*) a searchable term.
The list defined by map fib [0..] is defined as part of the definition of the function, rather than being created each time the function is called. Due to laziness, though, the list is only "realized" as necessary for any given call.
Say your first call is memoized_fib 10. This will cause the first 10 Fibonacci numbers to actually be computed and stored in memory, and they will stay in memory for the duration of the program. Subsequent calls with a smaller argument don't need to compute anything; subsequent calls with larger arguments need only compute those elements that occur later in the list than the largest existing element.

Non-pointfree style is substantially slower

I have the following, oft-quoted code for calculating the nth Fibonacci number in Haskell:
fibonacci :: Int -> Integer
fibonacci = (map fib [0..] !!)
where fib 0 = 0
fib 1 = 1
fib n = fibonacci (n-2) + fibonacci (n-1)
Using this, I can do calls such as:
ghci> fibonacci 1000
and receive an almost instantaneous answer.
However, if I modify the above code so that it's not in pointfree style, i.e.
fibonacci :: Int -> Integer
fibonacci x = (map fib [0..] !!) x
where fib 0 = 0
fib 1 = 1
fib n = fibonacci (n-2) + fibonacci (n-1)
it is substantially slower. To the extent that a call such as
ghci> fibonacci 1000
hangs.
My understanding was that the above two pieces of code were equivalent, but GHCi begs to differ. Does anyone have an explanation for this behaviour?
To observe the difference, you should probably look at Core. My guess that this boils down to comparing (roughly)
let f = map fib [0..] in \x -> f !! x
to
\x -> let f = map fib [0..] in f !! x
The latter will recompute f from scratch on every invocation. The former does not, effectively caching the same f for each invocation.
It happens that in this specific case, GHC was able to optimize the second into the first, once optimization is enabled.
Note however that GHC does not always perform this transformation, since this is not always an optimization. The cache used by the first is kept in memory forever. This might lead to a waste of memory, depending on the function at hand.
I tried to find it but struck out. I think I have it on my PC at home.
What I read was that functions using fixed point were inherently faster.
There are other reasons for using fixed point. I encountered one in writing this iterative Fibonacci function. I wanted to see how an iterative version would perform then I realized I had no ready way to measure. I am a Haskell neophyte. But here is an iterative version for someone to test.
I could not get this to define unless I used the dot after the first last function.
I could not reduce it further. the [0,1] parameter is fixed and not to be supplied as a parameter value.
Prelude> fib = last . flip take (iterate (\ls -> ls ++ [last ls + last (init ls)]) [0,1])
Prelude> fib 25
[0,1,1,2,3,5,8,13,21,34,55,89,144,233,377,610,987,1597,2584,4181,6765,10946,17711,28657,46368,75025]

Par function underlying logic

How does the par function work? Its signature is:
par :: a -> b -> b.
But this is strange. Why isn't it:
par :: (a -> b) -> a -> b
(get function, execute it in new thread and return result) ?
Another question, is this normal haskell multithreading??
par is for speculative parallelism, and relies on laziness.
You speculate that the unevaluated a should be computed while you're busy working on b.
Later in your program you might refer to a again, and it will be ready.
Here's an example. We wish to add 3 numbers together. Each number is expensive to compute. We can compute them in parallel, then add them together:
main = a `par` b `par` c `pseq` print (a + b + c)
where
a = ack 3 10
b = fac 42
c = fib 34
fac 0 = 1
fac n = n * fac (n-1)
ack 0 n = n+1
ack m 0 = ack (m-1) 1
ack m n = ack (m-1) (ack m (n-1))
fib 0 = 0
fib 1 = 1
fib n = fib (n-1) + fib (n-2)
Because we don't "execute functions" in Haskell. We evaluate values, that's how we control processor activity. What par x y does is basically: while evaluating the result y, the runtime will also already pre-evaluate x though that's itself not asked for yet.
Note that this isn't necessarily the nicest way of writing parallel code now. Check out newer alternatives like the Eval monad. You may want to read Simon Marlow's book.
In addition to previous answers it is worth pointing out that a and b will be evaluated to weak head normal form (WHNF) only (i.e. applying the outermost reduction or constructor), so it could be useful to force evaluation using deepseq.
In terms of operational semantics par creates a spark which is a pointer to a thunk (unevaluated computation) and added to the spark pool. This is very cheap and it is possible to have millions of sparks. Thread creation is advisory, the run time system can decide not to turn a into thread and prune superfluos parallelism by ignoring the spark or by subsuming the child spark in the parent.
The picture you show could indicate an issue with your code, where thread executed on CPU2 has significantly less work to do (load imbalance).

What does "!!" mean in haskell?

There are two functions written in the Haskell wiki website:
Function 1
fib = (map fib' [0 ..] !!)
where
fib' 0 = 0
fib' 1 = 1
fib' n = fib (n - 1) + fib (n - 2)
Function 2
fib x = map fib' [0 ..] !! x
where
fib' 0 = 0
fib' 1 = 1
fib' n = fib (n - 1) + fib (n - 2)
What does the "!!" mean?
This is actually more difficult to read then it would seem at first as operators in haskell are more generic then in other languages.
The first thing that we are all thinking of telling you is to go look this up yourself. If you do not already know about hoogle this is the time to become familiar with it. You can ask it either to tell you what a function does by name or (and this is even more cool) you can give it the type of a function and it can offer suggestions on which function implement that type.
Here is what hoogle tells you about this function (operator):
(!!) :: [a] -> Int -> a
List index (subscript) operator, starting from 0. It is an
instance of the more general genericIndex, which takes an index
of any integral type.
Let us assume that you need help reading this. The first line tell us that (!!) is a function that takes a list of things ([a]) and an Int then gives you back one of the thing in the list (a). The descriptions tells you what it does. It will give you the element of the list indexed by the Int. So, xs !! i works like xs[i] would in Java, C or Ruby.
Now we need to talk about how operators work in haskell. I'm not going to give you the whole thing here, but I will at least let you know that there is something more here then what you would encounter in other programming languages. Operators "always" take two arguments and return something (a -> b -> c). You can use them just like a normal function:
add x y
(+) x y -- same as above
But, by default, you can also use them between expression (the word for this is 'infix'). You can also make a normal function work like an operator with backtics:
x + y
x `add` y -- same as above
What makes the first code example you gave (especially for new haskell coders) is that the !! operator is used as a function rather then in a typical operator (infix) position. Let me add some binding so it is clearer:
-- return the ith Fibonacci number
fib :: Int -> Int -- (actually more general than this but do't worry about it)
fib i = fibs !! i
where
fibs :: [Int]
fibs = map fib' [0 ..]
fib' :: Int -> Int
fib' 0 = 0
fib' 1 = 1
fib' n = fib (n - 1) + fib (n - 2)
You can now work your way back to example 1. Make sure you understand what map fib' [0 ..] means.
I'm sorry your question got down-voted because if you understood what was going on the answer would have been easy to look up, but if you don't know about operators as the exist in haskell it is very hard to mentally parse the code above.
(!!) :: [a] -> Int -> a
List index (subscript) operator, starting from 0. It is an instance of the more general genericIndex, which takes an index of any integral type.
See here: http://www.haskell.org/ghc/docs/latest/html/libraries/base/Data-List.html#g:16

Memoization with recursion

I am trying to understand Haskell realization of memoization , but I don't get how it works:
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)
First of all I even don't understand why 'map'-function get three parameters (function - fib, list [0..], and ||), but not two how it must do.
Updated:
I have tried to rewrite the code, but get the different result:
f' :: (Int -> Int) -> Int -> Int
f' mf 0 = 0
f' mf 1 = 1
f' mf n = mf(n - 2) + mf(n - 1)
f'_list :: [Int]
f'_list = map (f' faster_f') [0..]
faster_f' :: Int -> Int
faster_f' n = f'_list !! n
Why? Is the any error in my reasoning?
First: Haskell supports operator sections. So (+ 2) is equal to \ x -> x + 2. This means the expression with map is equal to \ x -> map fib [0..] !! x.
Secondly, how this works: this is taking advantage of Haskell's call-by-need evaluation strategy (its laziness).
Initially, the list which results from the map is not evaluated. Then, when you need to get to some particular index, all the elements up to that point get evaluated. However, once an element is evaluated, it does not get evaluated again (as long as you're referring to the same element). This is what gives you memoization.
Basically, Haskell's lazy evaluation strategy involves memoizing forced values. This memoized fib function just relies on that behavior.
Here "forcing" a value means evaluating a deferred expression called a thunk. So the list is basically initially stored as a "promise" of a list, and forcing it turns that "promise" into an actual value, and a "promise" for more values. The "promises" are just thunks, but I hope calling it a promise makes more sense.
I'm simplifying a bit, but this should clarify how the actual memoization works.
map does not take three parameters here.
(map fib [0..] !!)
partially applies (slices) the function (!!) with map fib [0..], a list, as its first (left-hand) argument.
Maybe it's clearer written it as:
memoized_fib n = (map fib [0..]) !! n
so it's just taking the nth element from the list, and the list is evaluated lazily.
This operator section stuff is exactly the same as normal partial application, but for infix operators. In fact, if we write the same form with a regular function instead of the !! infix operator, see how it looks:
import Data.List (genericIndex)
memoized_fib :: Int -> Integer
memoized_fib = genericIndex (map fib [0..])
where fib 0 = 0
fib 1 = 1
fib n = memoized_fib(n - 2) + memoized_fib(n - 1)

Resources