1

这是一个创建 1MInt数字并将它们放在列表中的代码。

main = do
  let l =  [1..1000000]
  putStrLn $ show $ sum (foldl (\aux p -> p:aux) [] l)

(我知道它可能更优化(sum在 中fold),但我的观点不同。)看看这个版本

import qualified Data.ByteString.Lazy.Char8 as B
import qualified Data.ByteString.Lazy.Builder as Builder
import Data.ByteString.Lazy.Builder.ASCII
import Data.Maybe
import Data.List

main = do
    let l = map (Builder.toLazyByteString . intDec ) [1..1000000]
    let l2 = map (fst . fromJust . B.readInt) l
    putStrLn $ show $ sum (foldl' (\aux p -> p:aux) [] l2)

这个版本需要90MB内存!为什么?这是一个分析输出

创建1M ByteString的内存消耗

紫色区域是什么?

编辑 阅读评论后,我想添加一些说明。

这是一个测验。我想在内存中保留 1M 个数字(我正在构建一个查找表)。所以我“想强制将整个列表保存在内存中”。但我不想保留字节串。我的小代码模拟了我从磁盘加载字节串,将其转换为整数并将整数保存在内存中的情况。(这是我的目标)。但不知何故,字节串仍保留在内存中。为什么?

4

2 回答 2

2

问题是这里的懒惰。

main = do
    let l = map (Builder.toLazyByteString . intDec ) [1..1000000]
    let l2 = map (fst . fromJust . B.readInt) l
    putStrLn $ show $ sum (foldl' (\aux p -> p:aux) [] l2)

保持ByteStrings 直到sum强制对 thunkfst . fromJust . B.readInt进行评估。在此之前,您所拥有的是一个 thunk 列表,每个 thunk 都引用一个 short ByteString- 反转强制整个 thunk + ByteStrings 列表立即进入内存。

如果您不想保留ByteStrings,请强制评估 thunk,

main = do
    let l = map (Builder.toLazyByteString . intDec ) [1..1000000]
    let l2 = map (fst . fromJust . B.readInt) l
    putStrLn $ show $ sum (foldl' (\aux p -> p `seq` (p:aux)) [] l2)

产生的内存使用量要小得多,只需要简单的Ints 列表:

在此处输入图像描述

于 2013-08-21T21:11:04.427 回答
1

当我运行你的代码时,我得到了一个堆栈空间溢出异常。因此,我查看了可能导致它的原因,您对sum. 现在,我知道您永远不必怀疑内置函数,但是这个特定的函数很糟糕,因为它不会在列表中计算部分结果。如果你使用

mySum :: Num a => [a] -> a
mySum xs = go 0 xs
    where
        go accum [x]    = accum + x
        go accum (x:xs) = go s xs where !s = accum + x

相反(并启用爆炸模式扩展,因为它们更容易),然后它以 44 kB 运行。

编辑:我显然很笨,只是用 -O2 编译就可以sum优化到严格的版本。但是,您的中间列表会foldl' (\aux p -> p:aux) [] l2占用大量 RAM,因为它正在构建整个列表。

于 2013-08-21T12:42:00.160 回答