惰性数据类型的内存使用

Memory usage for lazy datatypes

我编写了一个程序来分析文件中的数据并对其执行操作。我的第一个实现使用 Data.ByteString 来读取文件的内容。然后使用 Data.Vector.Unboxed 将该内容转换为样本向量。然后我对这个(未装箱的)样本值向量执行处理和分析。

作为一个实验,我想知道如果我利用Haskell的懒惰会发生什么。我决定使用 Data.ByteString.Lazy 代替 Data.ByteString 并使用 Data.Vector 代替 Data.Vector.Unboxed 来做这个简单的测试。我希望看到内存使用情况有所改善。即使我的程序最终需要知道每个样本的值,我仍然希望内存使用量逐渐增加。当我分析我的程序时,结果让我感到惊讶。

我的原始版本在大约 20 毫秒内完成,其内存使用情况如下所示: 对我来说,这看起来像是懒惰的行为。示例似乎已加载到内存中,因为我的程序需要它们。

使用 Data.VectorData.ByteString 得到以下结果: 这看起来与懒惰行为相反。所有样本似乎都被一次加载到内存中,然后一个一个地删除。

我怀疑这与我对 BoxedUnboxed 类型的误解有关,所以我尝试将 Data.ByteString.Lazy 与 `Data.Vector.Unboxed' 一起使用。这是结果: 我不知道如何解释我在这里看到的东西。

谁能解释一下我得到的结果?

编辑 我正在使用 hGet 从文件中读取,这给了我一个 Data.ByteString.Lazy。我通过以下函数将此 ByteString 转换为 Floats 的 Data.Vector

toVector :: ByteString -> Vector Float
toVector bs =  U.generate (BS.length bs `div` 3) $ \i ->
     myToFloat [BS.index bs (3*i), BS.index bs (3*i+1), BS.index bs (3*i+2)]
  where
    myToFloat :: [Word8] -> Float
    myToFloat words = ...

浮点数用 3 个字节表示。

其余的处理主要包括对数据应用高阶函数(例如 filtermap 等)。

EDIT2 我的解析器包含一个函数,该函数从一个文件中读取所有数据,并 returns 样本向量中的这些数据(使用之前的 toVector 函数)。我已经编写了该程序的两个版本,一个带有 Data.ByteString,一个带有 Data.ByteString.Lazy。我用这两个版本进行了简单的测试:

main = do
  [file] <- getArgs
  samples <- getSamplesFromFile file
  let slice = V.slice 0 100000 samples
  let filtered = V.filter (>0) slice
  print filtered

严格版本给出了以下内存使用情况: 惰性版本给了我以下内存使用情况: 这个结果似乎与我的预期完全相反。有人可以解释一下吗? Data.ByteString.Lazy 有什么问题?

您正在对惰性字节串使用 length。这将需要整个字符串。如果那是输入惰性字节串的唯一用途,垃圾收集可以使其在常量 space 中工作。但是,您之后访问该字符串以进行进一步计算,从而迫使整个数据保留在内存中。

解决这个问题的方法是完全避免 length,并尝试折叠惰性字节串(仅一次!)以便流媒体可以完成它的工作。

例如,您可以做类似的事情

myread :: ByteString -> [Float]
myread bs = case splitAt 3 bs of
   ([x1,x2,x3], end) -> myToFloat x1 x2 x3 : myread end
   -- TODO handle shorter data as well

toVector bs = U.fromList $ myread bs

可能有更好的方法来利用 Vector 东西。 U.unfoldr 看起来很有希望。

我们目前拥有的数据不足以重现该问题。这里我 运行 四个版本 http://sprunge.us/PeIJ 将 strict 更改为 lazy,将 boxed 更改为 unboxed。我正在编译 ghc -O2 -rtsopts -prof 唯一值得一提的区别是 Data.Vector 版本中向量或流中的每个真实(指针)元素都指向自身外部一个漂亮的盒装 Haskell 浮点数占了一堆space。一切都基本相同,除了 Data.Vector 程序,正如预期的那样,在顶部有一大堆蓝色用于这些精心包装的花车。

编辑 这是我使用 ghc -prof -rtsopts

得到的结果