My space leak happens in one of my personal project. But I don't want someone to solve it in my project. I want to understand it.
I reproduced my space leak by making up this algorithm:
u is a sequence defined by:
- u(0) = 1
- u(1) = 2
- u(2) = 1
- u(4) = 3
- …
- u(19) = 11
after this, u is defined: u(n) = u(n-5) + u(n-10) - u(n-15)
Easy to implement in haskell right?
import System.Environment (getArgs)
u = [1, 2, 1, 3, 1, 4, 1, 5, 1, 6, 1, 7, 1, 8, 1, 9, 1, 10, 1, 11]
++ zipWith3 go u' u'' u'''
where u' = drop 15 u
u'' = drop 10 u
u''' = drop 5 u
go a b c = a + b - c
main = do
args <- getArgs
let n = read $ args !! 0
putStrLn $ show $ u !! n
Unfortunately, this space leaks:
$ time ./algo 9999999
Stack space overflow: current size 8388608 bytes.
Use `+RTS -Ksize -RTS' to increase it.
1.17user 0.19system 0:01.37elapsed 100%CPU (0avgtext+0avgdata 865124maxresident)k
0inputs+0outputs (0major+215695minor)pagefaults 0swaps
It looks like haskell is caching the whole list, where as I want it to cache only the 20 last elements.
For example, here's my implementation in C:
#include <stdint.h>
#include <stdio.h>
int main(int argc, char **argv)
{
size_t cursor;
int64_t buffer[20] = {1, 2, 1, 3, 1, 4, 1, 5, 1, 6, 1, 7, 1, 8, 1, 9, 1, 10, 1, 11};
int n = atoi(argv[1]);
for (cursor = 20; cursor <= n; cursor++) {
buffer[cursor%20] = buffer[(cursor+20-5)%20] + buffer[(cursor+20-10)%20] - buffer[(cursor+20-15)%20];
}
printf("%d\n", buffer[n%20]);
return 0;
}
$ ./a.out 9999999
5000001
My C implementation uses O(n) time and O(1) space. But it looks like my haskell implementation is using O(n) space.
Why is Haskell able to figure it out for fibonnacci, but not for my made up sequence? What did I do wrong? How would you implement this algorithm in Haskell?