Debugging 为什么GHC中直接导入的函数与我使用从GHC库复制的源代码编写的函数有如此大的不同
问题1:此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
过滤器
是从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