我的空间泄漏发生在我的一个个人项目中。但我不希望有人在我的项目中解决它。我想了解它。
我通过制作这个算法重现了我的空间泄漏:
u 是由以下定义的序列:
- u(0) = 1
- u(1) = 2
- u(2) = 1
- u(4) = 3
- …</li>
- u(19) = 11
在此之后,定义 u:u(n) = u(n-5) + u(n-10) - u(n-15)
在haskell中很容易实现对吧?
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
不幸的是,这个空间泄漏:
$ 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
看起来 haskell 正在缓存整个列表,而我希望它只缓存最后 20 个元素。
例如,这是我在 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
我的 C 实现使用 O(n) 时间和 O(1) 空间。但看起来我的 haskell 实现正在使用 O(n) 空间。
为什么 Haskell 能够为fibonnacci 计算出它,但不能为我编造的序列计算出来?我做错了什么?你将如何在 Haskell 中实现这个算法?