Debugging 为什么GHC中直接导入的函数与我使用从GHC库复制的源代码编写的函数有如此大的不同

Debugging 为什么GHC中直接导入的函数与我使用从GHC库复制的源代码编写的函数有如此大的不同,debugging,haskell,memory-leaks,garbage-collection,ghc-api,Debugging,Haskell,Memory Leaks,Garbage Collection,Ghc Api,问题1:此过滤器是从GHC的库中复制的,但与直接导入的过滤器消耗恒定数量的内存相比,为什么它消耗的内存越来越多 module Has (r,p,s) where import Prelude ((==),Bool(..),otherwise,(||),Eq) import qualified Data.List as L filter :: (a -> Bool) -> [a] -> [a] filter _pred [] = [] filter pred (x:xs

问题1:此
过滤器
是从
GHC
的库中复制的,但与直接导入的
过滤器
消耗恒定数量的内存相比,为什么它消耗的内存越来越多

module Has (r,p,s) where

import Prelude ((==),Bool(..),otherwise,(||),Eq)
import qualified Data.List as L

filter :: (a -> Bool) -> [a] -> [a]
filter _pred []    = []
filter pred (x:xs)
  | pred x         = x : filter pred xs
  | otherwise      = filter pred xs
问题2:此
过滤器
是从
GHC
的库中复制的,但为什么它会像直接使用的
elem
那样消耗越来越多的内存,与直接导入的
过滤器
相比,它也消耗越来越多的内存

elem :: (Eq a) => a -> [a] -> Bool
elem _ []       = False
elem x (y:ys)   = x==y || elem x ys
GHC版本:7.4.2 OS:Ubuntu12.10使用-O2编译以优化

由于上述
filter
elem
的定义意味着
p=filter(=10000000000)[0..]。但是
p
s
都消耗了越来越多的内存。使用直接导入的
过滤器定义的
r
消耗恒定数量的内存

module Has (r,p,s) where

import Prelude ((==),Bool(..),otherwise,(||),Eq)
import qualified Data.List as L

filter :: (a -> Bool) -> [a] -> [a]
filter _pred []    = []
filter pred (x:xs)
  | pred x         = x : filter pred xs
  | otherwise      = filter pred xs
我的问题是,为什么GHC中直接导入的函数与我使用从GHC库复制的源代码编写的函数有如此大的不同。我想知道GHC是否有问题

我还有一个问题:上面的代码是从我编写的一个项目中抽象出来的,该项目还面临着“消耗越来越多的内存,理论上应该进行垃圾收集”的问题。所以我想知道有没有一种方法可以找出GHC中哪个变量占用了这么多内存


感谢您的阅读。

ghci内存消耗的原因不是
过滤器的代码
元素
。(尽管
GHC.List
中的
filter
的重写规则通常会使它更好一些。)

让我们看一下使用
-O2
-ddump siml
)生产的核心ghc-7.4.2(部分)。对于
r
,首先使用
GHC.List.filter

r = L.filter (==1000000000000) [0..]
p = filter (==1000000000000) [0..]
s = 1000000000000 `elem` [0..]
如果
fun
是内联的,这可能会更有效率,但关键是要
filter
ed的列表本身并不存在,它被融合掉了

另一方面,对于
p
,如果没有重写规则,我们得到

Has.p1::[GHC.Integer.Type.Integer]
[GblId,
Unf=Unf{Src=,TopLvl=True,Arity=0,Value=False,
ConLike=False,廉价=False,可扩展=False,
指导=如果参数[]30 0}]
Has.p1=GHC.Enum.enumDeltaInteger Has.p3 Has.p2
Has.p::[GHC.Integer.Type.Integer]
[GblId,
Str=DmdType,
Unf=Unf{Src=,TopLvl=True,Arity=0,Value=False,
ConLike=False,廉价=False,可扩展=False,
指导=如果参数[]30 0}]
Has.p=Has.filter@GHC.Integer.Type.Integer Has.p4 Has.p1
列表
[0..]
Has.p1
)的顶级CAF和
Has.filter
应用于
(==1000000000000)
和列表

所以这一个确实创建了要过滤的实际列表-因此它的效率较低

但通常情况下(运行编译的二进制文件),这在内存消耗方面没有问题,因为列表在被消耗时会被垃圾收集。然而,出于我无法理解的原因,ghci在评估
p
s
时确实保留了列表
[0..](评估
s
,因此列表单元格只有一个可能的源。使用
+RTS-M300M-hT-RTS
调用ghci,因此在内存使用量达到300M后不久,ghci终止):

因此,ghci中内存消耗的原因是要筛选的列表的硬编码。如果在提示时使用
Has.filter
,则内存使用量与预期一致


我不确定ghci是否保留列表
[0..]
是一个bug或预期行为。

GHC版本,操作系统?我不能像那样复制内存增长实现
elem
。从GHC库复制?实际上,其中包含的内容远不止这些定义,例如,这意味着您通常引用的定义将不会被使用。-也就是说,我也无法重新编写减少内存消耗问题。你使用什么优化标志?我想他指的是标准前奏曲中的定义。顺便说一句,也不能重现这个问题。请注意,你简化实际代码的确切方式可能会产生很大的不同!例如,“大列表”
[0..]
r
p
s
重新计算;或者它是以某种方式共享的(作为顶级定义或函数的参数?)我想我们将不得不看到更多的代码…您使用
-O2
编译它,但当内存消耗增加时,您在ghci中运行它,不是吗?
r = go fun 0 1
  where
    go foo x d = x `seq` (x `foo` (go foo (x+d) d))

fun n list
    | n == 1000000000000 = n : list
    | otherwise          = list
Has.p1 :: [GHC.Integer.Type.Integer]
[GblId,
 Unf=Unf{Src=<vanilla>, TopLvl=True, Arity=0, Value=False,
         ConLike=False, Cheap=False, Expandable=False,
         Guidance=IF_ARGS [] 30 0}]
Has.p1 = GHC.Enum.enumDeltaInteger Has.p3 Has.p2

Has.p :: [GHC.Integer.Type.Integer]
[GblId,
 Str=DmdType,
 Unf=Unf{Src=<vanilla>, TopLvl=True, Arity=0, Value=False,
         ConLike=False, Cheap=False, Expandable=False,
         Guidance=IF_ARGS [] 30 0}]
Has.p = Has.filter @ GHC.Integer.Type.Integer Has.p4 Has.p1