Haskell优化:如何阻止列表执行分配?

时间:2018-03-09 01:21:38

标签: haskell optimization

在这个程序中:

select()

(编译并使用module Main where import Data.IntSet main = do print $ size $ {-# SCC "fromAscList" #-} fromAscList $ {-# SCC "generate_list" #-} [1..1000] 运行)

我从GHC分析器获得此输出(为简洁起见编辑):

stack ghc Main.hs -- -prof -fprof-auto && ./Main +RTS -p

这似乎表明 total alloc = 202,704 bytes (excludes profiling overheads) COST CENTRE MODULE SRC %time %alloc generate_list Main Main.hs:6:40-48 0.0 39.5 fromAscList Main Main.hs:(5,38)-(6,48) 0.0 36.2 main Main Main.hs:(3,1)-(6,48) 0.0 5.0 individual inherited COST CENTRE MODULE %time %alloc %time %alloc main Main 0.0 0.1 0.0 75.8 fromAscList Main 0.0 36.2 0.0 75.7 generate_list Main 0.0 39.5 0.0 39.5 在程序过程中分配80Kb(202,704字节的39.5%),然后[1..a']再次分配相同的数量。 (输入Int的一个分配,以及存储的Int的另一个分配?)

两个问题:

  1. 这些"真实"来自fromAscList的分配,还是会重复使用同一块内存? (我想知道堆分配是否可能成为瓶颈。)
  2. 如果这是重复使用堆,有没有办法将未装箱的值1..1000直接流式传输到generate_list

1 个答案:

答案 0 :(得分:2)

您应该使用fromDistinctAscList,否则必须将每个下一个Int与其前任进行比较,以确定是添加还是跳过,这必然会产生您观察到的开销。 (虽然我没有查看源代码,但似乎有理由猜测会发生这种情况,在这种情况下我们很难期望以最佳方式分配内存。)

试试这段代码:

main = do
   print $ size
         $ {-# SCC "fromDistinctAscList" #-} fromDistinctAscList
         $ {-# SCC "generate_list" #-} [1..1000]

我观察到的表现是记忆力提高了36%。 (这个数字似乎与你的一致。)以下是分析报告的相关部分:

COST CENTRE            MODULE                SRC                        no.     entries  %time %alloc   %time %alloc
....
  main                 ListAlloc             ListAlloc.hs:(5,1)-(8,55)  276          1    0.0    0.2     0.0   62.5
   fromDistinctAscList ListAlloc             ListAlloc.hs:(7,46)-(8,55) 278          1    0.0    1.1     0.0   62.4
    generate_list      ListAlloc             ListAlloc.hs:8:40-55       279          1    0.0   61.2     0.0   61.2
...

关于分配的实际情况,我不确定我是否理解这个问题,但是,查看带有+RTS -S的垃圾收集,两种变体一直在几乎相同的内存中运行,由于增长而线性增长IntMap。似乎所有其他计算都是在相当小的常量内存中完成的。