完整的源代码和分析报告位于:https://gist.github.com/anonymous/92334d00859c3db0ba8a
我正在尝试以文本PPM图像文件的形式生成一个大文本文件,我遇到了一些性能问题。我可以使用其他图像格式,但文本生成性能让我感兴趣,因为我有其他类似的情况,我没有选择替代格式的灵活性。
我开始将String
连接起来生成我的文本文件,并很快发现它占用了近90%的执行时间。所以,我切换到Data.Text
,但发现性能没有显着提高。我最终生成了一个测试文件,试图找出问题,比较两个函数:
ppmS (Matrix mx) = unlines ["P3", show w', show w', "255", pixels]
where
w' = mxSize mx * scale
pixels = unlines $ map row [0..w'-1]
row j = intercalate " " $ map (pix j) [0..w'-1]
pix j i = color (mx ! (div i scale, div j scale))
ppmT (Matrix mx) = T.unlines ["P3", T.pack (show w'), T.pack (show w'), "255", pixels]
where
w' = mxSize mx * scale
pixels = T.unlines $ map row [0..w'-1]
row j = T.intercalate " " $ map (pix j) [0..w'-1]
pix j i = color (mx ! (div i scale, div j scale))
使用以下命令在探查器中运行它:
ghc -O2 --make -prof -auto-all -caf-all -fforce-recomp test.hs
./test +RTS -p
我看到以下内容:
total time = 0.60 secs (597 ticks @ 1000 us, 1 processor)
total alloc = 1,162,898,488 bytes (excludes profiling overheads)
individual inherited
COST CENTRE MODULE no. entries %time %alloc %time %alloc
MAIN MAIN 96 0 0.0 0.0 100.0 100.0
main Main 193 0 24.1 14.7 24.1 14.7
CAF:main3 Main 188 0 0.0 0.0 39.9 37.0
main Main 225 0 0.0 0.0 39.9 37.0
main.ppmFromText Main 226 0 0.0 0.0 39.9 37.0
ppmT Main 227 0 8.5 9.3 39.9 37.0
ppmT.row Main 252 0 0.0 0.0 0.0 0.0
ppmT.pixels Main 250 1 8.7 9.3 31.3 27.7
ppmT.row Main 251 500 20.6 18.4 22.6 18.4
ppmT.pix Main 253 250000 1.8 0.0 2.0 0.0
color Main 254 250000 0.2 0.0 0.2 0.0
CAF:main6 Main 171 0 0.0 0.0 35.8 48.3
main Main 198 0 0.0 0.0 35.8 48.3
main.ppmFromString Main 199 0 0.0 0.0 35.8 48.3
ppmS Main 200 0 9.4 14.4 35.8 48.3
ppmS.pixels Main 216 1 8.5 14.5 26.5 33.9
ppmS.row Main 217 500 13.9 19.4 17.9 19.4
ppmS.pix Main 218 250000 3.5 0.0 4.0 0.0
color Main 219 250000 0.5 0.0 0.5 0.0
告诉我,Text
和String
版本都占用了大量时间并分配了大量内存。
生成此文本的更好方法是在时间和效率方面提高效率。记忆?
答案 0 :(得分:3)
更新:事实证明,只需使用ByteStrings代替Text:
import qualified Data.ByteString.Char8 as T
import qualified Data.ByteString as TI
实现了与我最初尝试的Blaze方法相同甚至更好的性能。请参阅最后的更新统计信息表。
原始答案:
使用blaze builder monoid可以获得更好的效果。
以下是适合使用Blaze.ByteString.Builder
的算法:
import Blaze.ByteString.Builder
import Blaze.ByteString.Builder.Char8
import Data.Monoid
import Data.List (intersperse)
munlines = mconcat . map ( <> (fromChar '\n') )
mintercalate s xs = mconcat $ intersperse s xs
ppmB (Matrix mx) = munlines [ fromString "P3",
fromString (show w'),
fromString (show w'),
fromString "255",
pixels ]
where
w' = mxSize mx * scale
pixels = munlines $ map row [0..w'-1]
row j = mintercalate (fromString " ") $ map (pix j) [0..w'-1]
pix j i = fromString $ color (mx ! (div i scale, div j scale))
main = do
let m = makeMatrix
let ppmFromString = toLazyByteString $ ppmB m
LBS.writeFile "output.ppm" ppmFromString
完整资源here。
在我的机器上,我得到以下四种版本的RTS统计数据:
Allocated Time %GC
string 561 MB 0.40 s 56.6 %
text 601 MB 0.25 s 6.9 %
blaze 95 MB 0.07 s 3.0 %
bytestring 91 MB 0.06 s 10.1 %
另一种选择是使用二进制包中的Put
monad。