我正在编写一个使用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
中的Get
monad调用 Data.Binary
:
main = do
binary <- BS.readFile "test.tga"
let (pixelMap, binary', nil) = runGetState (readPixelMap 2048 2048) binary 0
我一直在努力工作的印象是2048 x 2048 .TGA图像应加载到几百兆字节(最多)的内存中。有没有明显的方法来改善我的垃圾收集时间/分配金额?
答案 0 :(得分:12)
您的RGBColor
类型使用5个机器字,其中4个是指向每个Integer
的指针。每个Integer
包含一个用于GC /标记的机器字,以及用于小型表示的一个附加字,或者包含指向GMP数据的字节数组和肢体计数的指针的大型表示。
此外,您还使用列表列表来存储值。每个非空列表节点都是一个用于GC /标记的单词,一个指向该值的单词指针,以及一个指向尾部的单词指针。
要使用更少的内存,请使用适当的数据类型。尽可能使用未打包的值。对于8位值,请使用Word8
代替Integer
。使用数组而不是列表。你知道,像你一样关心记忆的基本知识。
查看http://johantibell.com/files/slides.pdf的一些基础知识。
答案 1 :(得分:2)
使用Integer
元组来存储颜色数据(总是32位数据)是没有意义的。为什么不使用以下任何变体:(Word8, Word8, Word8, Word8)
; data Colour = C Word8 Word8 Word8 Word
; data Colour = C Word32
懒惰的ByteString
只是严格ByteString
的列表。因此,如果您逐字节地读取数据,您将基本上生成一个严格ByteString
的列表,其长度为2048 * 2048。效率不高。
使用[[a]]
存储二维数组也是如此。就内存性能而言,这只是您可以使用的最差数据类型。尝试以下任何一个:Array (Int, Int) Color
; UArray (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