【发布时间】:2012-07-20 14:57:41
【问题描述】:
我有一段代码使用sequence 从概率分布中重复采样。从道德上讲,它会这样做:
sampleMean :: MonadRandom m => Int -> m Float -> m Float
sampleMean n dist = do
xs <- sequence (replicate n dist)
return (sum xs)
除了它有点复杂。我感兴趣的实际代码是函数likelihoodWeightingthis Github repo。
我注意到运行时间与n 呈非线性关系。特别是,一旦n 超过某个值,它就会达到内存限制,并且运行时间会爆炸。我不确定,但我认为这是因为 sequence 正在建立一长串 thunk,直到调用 sum 才得到评估。
一旦我超过了大约 100,000 个样本,程序就会慢下来。我想对此进行优化(我的感觉是 1000 万个样本应该不成问题)所以我决定对其进行分析 - 但我在理解分析器的输出时遇到了一点麻烦。
分析
我在文件main.hs 中创建了一个简短的可执行文件,它使用 100,000 个样本运行我的函数。这是做的输出
$ ghc -O2 -rtsopts main.hs
$ ./main +RTS -s
我注意到的第一件事 - 它分配了近 1.5 GB 的堆,并将 60% 的时间用于垃圾收集。这通常表示过于懒惰吗?
1,377,538,232 bytes allocated in the heap
1,195,050,032 bytes copied during GC
169,411,368 bytes maximum residency (12 sample(s))
7,360,232 bytes maximum slop
423 MB total memory in use (0 MB lost due to fragmentation)
Generation 0: 2574 collections, 0 parallel, 2.40s, 2.43s elapsed
Generation 1: 12 collections, 0 parallel, 1.07s, 1.28s elapsed
INIT time 0.00s ( 0.00s elapsed)
MUT time 1.92s ( 1.94s elapsed)
GC time 3.47s ( 3.70s elapsed)
RP time 0.00s ( 0.00s elapsed)
PROF time 0.23s ( 0.23s elapsed)
EXIT time 0.00s ( 0.00s elapsed)
Total time 5.63s ( 5.87s elapsed)
%GC time 61.8% (63.1% elapsed)
Alloc rate 716,368,278 bytes per MUT second
Productivity 34.2% of total user, 32.7% of total elapsed
以下是结果
$ ./main +RTS -p
第一次运行时,发现有一个函数被重复调用,结果我可以记忆它,这使事情加速了 2 倍。它没有解决空间泄漏,但是。
COST CENTRE MODULE no. entries %time %alloc %time %alloc
MAIN MAIN 1 0 0.0 0.0 100.0 100.0
main Main 434 4 0.0 0.0 100.0 100.0
likelihoodWeighting AI.Probability.Bayes 445 1 0.0 0.3 100.0 100.0
distributionLW AI.Probability.Bayes 448 1 0.0 2.6 0.0 2.6
getSampleLW AI.Probability.Bayes 446 100000 20.0 50.4 100.0 97.1
bnProb AI.Probability.Bayes 458 400000 0.0 0.0 0.0 0.0
bnCond AI.Probability.Bayes 457 400000 6.7 0.8 6.7 0.8
bnVals AI.Probability.Bayes 455 400000 20.0 6.3 26.7 7.1
bnParents AI.Probability.Bayes 456 400000 6.7 0.8 6.7 0.8
bnSubRef AI.Probability.Bayes 454 800000 13.3 13.5 13.3 13.5
weightedSample AI.Probability.Bayes 447 100000 26.7 23.9 33.3 25.3
bnProb AI.Probability.Bayes 453 100000 0.0 0.0 0.0 0.0
bnCond AI.Probability.Bayes 452 100000 0.0 0.2 0.0 0.2
bnVals AI.Probability.Bayes 450 100000 0.0 0.3 6.7 0.5
bnParents AI.Probability.Bayes 451 100000 6.7 0.2 6.7 0.2
bnSubRef AI.Probability.Bayes 449 200000 0.0 0.7 0.0 0.7
这是一个堆配置文件。我不知道为什么它声称运行时间是 1.8 秒 - 这次运行大约需要 6 秒。
谁能帮我解释分析器的输出 - 即确定瓶颈在哪里,并就如何加快速度提供建议?
【问题讨论】:
-
尝试使用
replicateM n而不是sequence . replicate n -
运行时间没有变化 - 可能并不奇怪,因为
replicateM n是 defined assequence . replicate n。 -
length xs总是n,不是吗?因此,将(length xs)替换为n,然后xs不必随时完全驻留在内存中。 -
@dave4420 也可能触发列表融合优化,因为
sum是列表使用者,replicateM是列表生成器。这也将消除对额外严格性的需求。 -
@PeterHall 您将同一个生成器传递给对
randomR的每次调用,因此它每次都会生成相同的“随机”数字。您可以尝试类似(我没有测试过)main = do { a <- sampleMean 10000 (randomRIO (0.0,1.0)); print a }
标签: performance haskell strictness