当使用Haskell解析二进制文件时,如何减少垃圾回收?

7
我正在使用Haskell编写一个程序来解析.TGA文件,但是性能非常差。一个2048 x 2048像素的图像需要几秒钟才能解析。
我使用了+RTS -p -RTS运行我的代码,并从报告中得到了以下有趣的信息:
total time = 1.08 secs
total alloc = 3,037,568,120 bytes

COST CENTRE           MODULE    %time    %alloc
readPixelMap            Main     33.0      11.0
readPixelMap.getByte    Main     32.7      75.1
readPixelMap.getColor   Main     27.0      13.3

看起来我的程序在 readPixelMap 函数中分配了 大量 的内存。该函数如下:

readPixelMap width height = do
    pixels <- replicateM height (replicateM width getColor)
    return $ PixMap pixels
    where getByte = liftM toInteger getWord8
          getColor = do (red:green:blue:alpha:xs) <- replicateM 4 getByte
                        return (red, green, blue, alpha)

一个PixMap被定义为

data PixMap = PixMap [[RGBAColor]] deriving (Show)
type RGBAColor = (Integer, Integer, Integer, Integer)

readPixelMap 是使用来自 Data.BinaryGet 单子调用的:

main = do
    binary <- BS.readFile "test.tga"
    let (pixelMap, binary', nil) = runGetState (readPixelMap 2048 2048) binary 0

我一直以为一个2048 x 2048的.TGA图像应该只需要几百兆内存(最多)。有没有明显的方法来提高我的垃圾收集时间/分配数量?

2个回答

11

您的RGBColor类型使用5个机器字,其中4个是指向每个Integer的指针。每个Integer由一个用于GC/标记的机器字和一个用于小表示或大表示的单词组成,大表示包括一个指向GMP数据的字节数组的指针和一个枝数。

此外,您正在使用列表嵌套列表来存储值。每个非空列表节点都是一个用于GC/标记的字,一个指向值的指针以及一个指向尾部的指针。

为了使用更少的内存,请使用适当的数据类型。在可能的情况下使用未打包的值。对于8位值,请使用Word8而不是Integer。使用数组而不是列表。这些都是关于注意内存使用的基础知识。

请查看http://johantibell.com/files/slides.pdf获取一些基本信息。


2
  1. 使用Integer元组来存储颜色数据(始终是32位数据)是没有意义的。为什么不使用以下任何一种变体:(Word8, Word8, Word8, Word8)data Colour = C Word8 Word8 Word8 Worddata Colour = C Word32

  2. 一个惰性的ByteString只是一个严格ByteString列表。因此,如果您逐字节读取数据,您将实际上生成一个长度为2048x2048的严格ByteString列表。效率不高。

  3. 对于使用[[a]]存储二维数组也可以这样说。就内存性能而言,这几乎是您可以使用的最糟糕的数据类型。尝试使用以下任何一种:Array (Int, Int) ColorUArray (Int, Int) Color(Int, Int, Ptr Word8)。我更喜欢Data.Array.Repa中的数组,它们具有高性能,具有许多内置遍历、折叠等功能,并自然支持并行化。另一个巨大的优势是Data.Array.Repa.Repr.ByteString.fromByteString将把严格的ByteString转换为一个数组以进行进一步操作。最好的部分是,当读取数组时,您不再需要担心性能;这已经为您完成了!

一个例子:

import Data.Array.Repa.Repr.ByteString
import Data.Array.Repa
import qualified Data.ByteString as B
import Data.Word (Word8)

data TGA = TGA (Array B DIM2 Word8)

readTGAFromFile :: FilePath -> (Int, Int) -> IO TGA
readTGAFromFile file (width,height) = do
  dat <- B.readFile file
  return $ TGA $ fromByteString (Z :. height :. width) dat

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接