我使用GHC 7.4编译以下函数:
nodups' :: [Int] -> Bool
nodups' = ok empty
where ok _ [] = True
ok seen (n:ns) = not (n `member` seen) && ok (n `insert` seen) ns
member n word = testBit word n
insert n word = setBit word n
empty = 0 :: Int
该函数在一个小整数列表中查找重复的元素。集合CCD_ 1是作为比特向量的一组小整数的表示。探查器(与ghc -prof -auto-all
一起运行)声称ok
函数占总分配的22%。看看-ddump-simpl
的输出,我不明白为什么要分配这个代码。我检查了一下,据我所知,是而不是为insert
的调用分配了一个thunk。
我应该看什么来识别代码中正在分配的部分?
一般
我知道函数式语言的简单(科学)实现,如果我没记错的话,有一个G-Machine可以与Haskell一起使用。
这意味着(如果我没有记错的话)你的程序状态被表示成一个"树",其中的节点是(为了简单起见)你在代码中使用的函数。叶子将是它的论据。"G-Maschine"然后沿着"Spine"(节点的左侧链)查找,并在一组可用的"Function"("Supercompinators"?)中查找它可以应用的模式匹配。如果从定义的左侧识别出mattern匹配,则该匹配将被定义的右侧替换。
这意味着即使是像这样的简单线路
ok seen (n:ns) = not (n `member` seen) && ok (n `insert` seen) ns
甚至
(n:ns) = ns
正在计算机内存中执行某些操作,即匹配模式
...
...
(:)
/
n ns
并将其替换为
...
...
ns
最终结果可能比输入消耗更少的内存,但这是一个动态步骤,因此必须在某个地方进行。如果这种情况一次又一次地重复(在"紧密循环"中),那么这将使您的CPU繁忙,也会使您的内存繁忙——仅仅因为G-Machine正在运行。(正如我所说,我不确定G-Machine-概念是否适用于此,但我想它是类似的)。
具体猜测
member n word = testBit word n
insert n word = setBit word n
除此之外,我还有一些怀疑。CCD_ 6和CCD_。如果是这样的话,可能需要一些工作。如果它们是适当的数组,那也没关系。如果它们是一种映射或集合。。。好可能会涉及到代价高昂的哈希?或者通过使用大量(昂贵的?)比较操作的平衡树来实现?