如何强制haskell不存储整个bytestring?

时间:2011-02-16 14:30:47

标签: optimization haskell compression ghc huffman-code

我在学术目的上在haskell上写了一个小的(相对)应用程序。我正在基于此代码http://www.haskell.org/haskellwiki/Toy_compression_implementations实现霍夫曼压缩。

此代码的变体是https://github.com/kravitz/har/blob/a5d221f227c27fd1c5587217a29a169a377521a6/huffman.hs,它使用延迟字节串。当我实现RLE压缩时,一切都很顺利,因为它只需一步即可处理输入流。但是霍夫曼处理它两次,因此我有一个存储在内存中的计算字节串,这对于一个大文件是不好的(但对于相对较小的文件,它在堆中也分配了太多的空间)。这不仅是我的怀疑,因为分析也表明大多数堆由bytestring分配吃掉。

此外,我在文件中串行化了一个流长度,它也可能导致在内存中加载完整的字节串。是否有任何简单的方法可以说ghc善良并重新评估流几次?

2 个答案:

答案 0 :(得分:4)

您可以传递计算字节字符串的内容,而不是将字节字符串传递给编码器,然后在每次需要时显式重新计算该值。

compress :: ST s ByteString -> ST s ByteString
compress makeInput = do
  len      <- (return $!) . ByteString.length =<< makeInput
  codebook <- (return $!) . makeCodebook      =<< makeInput
  return . encode len codebook                =<< makeInput

compressIO :: IO ByteString -> IO ByteString
compressIO m = stToIO (compress (unsafeIOToST m))

compress的参数实际上应该计算该值。简单地用return包装值是行不通的。此外,对makeInput的每次调用实际上必须对其结果进行评估,否则在重新计算输入时,将在内存中保留一个惰性的,未评估的输入副本。

正如barsoap所说,通常的方法是一次压缩一个块。

答案 1 :(得分:3)

当(Huffmann-)压缩时的常用方法,因为无法绕过处理输入两次,一次收集概率分布,一次进行实际压缩,就是将输入分块并压缩每个人分开。虽然这仍然会占用记忆,但它只会最大限度地吃掉一定量。

也就是说,您可能希望查看bytestring-mmap,但这对于不支持mmap的文件系统支持的标准输入,套接字和其他文件描述符不起作用。 / p>

您还可以在收集概率分布后重新读取文件中的字节串(再次,如果您没有从任何类似管道接收它),但这仍然会使您的代码在1TB文件上挽救。 / p>