Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/10.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/blackberry/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Haskell GHC解释器中冗余使用seq的空间泄漏_Haskell_Ghc_Space Leak - Fatal编程技术网

Haskell GHC解释器中冗余使用seq的空间泄漏

Haskell GHC解释器中冗余使用seq的空间泄漏,haskell,ghc,space-leak,Haskell,Ghc,Space Leak,我在解释器中键入此代码,内存会迅速消耗: last [1..10^7] `seq` () 我不明白为什么这需要超过O(1)个空间。如果我这样做(应该是一样的,因为Show强制弱头范式,所以seq是多余的?) …很好用 我无法在口译员之外再现这种情况 这是怎么回事 以下是一些测试用例: 注意事项: 通过在解释器中运行,我加载wtf.hs而不编译它,并在ghci中键入wtf 通过编译,我可以ghc——生成wtf.hs&./wtf last可以替换具有严格累加器的sum,或在列表中查找max元

我在解释器中键入此代码,内存会迅速消耗:

last [1..10^7] `seq` ()
我不明白为什么这需要超过O(1)个空间。如果我这样做(应该是一样的,因为Show强制弱头范式,所以seq是多余的?)

…很好用

我无法在口译员之外再现这种情况

这是怎么回事


以下是一些测试用例:

注意事项:

  • 通过在解释器中运行,我加载wtf.hs而不编译它,并在ghci中键入
    wtf
  • 通过编译,我可以
    ghc——生成wtf.hs&./wtf
  • last
    可以替换具有严格累加器的
    sum
    ,或在列表中查找max元素的函数,空间泄漏仍然会发生
  • 我在使用
    $时没有见过这种行为而不是
    seq
  • 我尝试添加一个伪
    ()
    参数,因为我认为这可能是CAF问题。什么都不会改变
  • Enum
    上的函数可能没有问题,因为我可以使用
    wtf5
    和更高版本重现行为,而这些版本根本不使用
    Enum
  • Num
    Int
    Integer
    可能没有问题,因为我可以在
    wtf14
    wtf16
    中复制没有它们的行为
我尝试用Peano算法重现这个问题,从等式中提取列表和整数(在10^9的末尾提取),但在尝试实现
*

时遇到了其他共享/空间泄漏问题,我认为@n.m是正确的。 没有强制列表中的值,因此1+1+1+1+。。。thunk最终杀死了太空


我将快速进行测试。

我们需要查看整数的
enumFromTo
实例,最后:

last []                 =  errorEmptyList "last"
last (x:xs)             =  last' x xs
  where last' y []     = y
        last' _ (y:ys) = last' y ys
它在GHC.Enum中定义为:

enumFrom x             = enumDeltaInteger  x 1
enumFromThen x y       = enumDeltaInteger  x (y-x)
enumFromTo x lim       = enumDeltaToInteger x 1 lim
在哪里

正如所料,
last
是完全懒惰的

对于Integer Enum类,我们有一个严格的累加器(显式地)用于
enumFrom
。在有界情况下(例如,
[1..n]
),它调用
enumDeltaToInteger
,然后进入
up\u list
,它使用辅助程序展开列表,直到达到其限制

但是
go
帮助程序中的
up\u列表
x
中是严格的(请参见与
lim
的比较)

当在GHCi中运行时,这一切都没有得到优化,在返回
()
之前生成对enumFromTo的简单调用

那么,为什么我们要在
seq
案例中保留列表,而不是在常规案例中?常规情况在constrant空间中运行良好,这取决于
enumFromTo
对于
Integer
last
的惰性。该案例的GHCi核心如下所示:

let {
  it_aKj :: GHC.Integer.Type.Integer
  [LclId,
   Unf=Unf{Src=<vanilla>, TopLvl=False, Arity=0, Value=False,
           ConLike=False, Cheap=False, Expandable=False,
           Guidance=IF_ARGS [] 170 0}]
  it_aKj =
    GHC.List.last
      @ GHC.Integer.Type.Integer
      (GHC.Enum.enumFromTo
         @ GHC.Integer.Type.Integer
         GHC.Num.$fEnumInteger
         (GHC.Integer.smallInteger 1)
         (GHC.Real.^
            @ GHC.Integer.Type.Integer
            @ GHC.Integer.Type.Integer
            GHC.Num.$fNumInteger
            GHC.Real.$fIntegralInteger
            (GHC.Integer.smallInteger 10)
            (GHC.Integer.smallInteger 7))) } in
GHC.Base.thenIO
  @ ()
  @ [()]
  (System.IO.print
     @ GHC.Integer.Type.Integer GHC.Num.$fShowInteger it_aKj)
  (GHC.Base.returnIO
     @ [()]
     (GHC.Types.:
        @ ()
        (it_aKj
         `cast` (UnsafeCo GHC.Integer.Type.Integer ()
                 :: GHC.Integer.Type.Integer ~ ()))
        (GHC.Types.[] @ ())))
这使得它保留了价值

正如我们所看到的,
up\u list
的实现在累加器中是严格的(因为它与
lim
相比,并且列表是延迟展开的——因此
last
应该能够在恒定的空间中使用它)。用手写下这个表达式证实了这一点

执行ghci执行的堆配置文件显示保留的整个列表:

这至少告诉我们,这不是一连串的恶作剧,而是整个名单正在严格建立并一直保持,直到被丢弃

所以,谜团是:在ghci中,什么保留着列表参数的
last
,而在ghc中没有


我现在怀疑ghci的一些内部(或细微)细节——我认为这值得一张ghci罚单。

这可能与此有关吗?@ChrisWong好吧,如果类型默认是罪魁祸首,那么修复诸如
seq(last[1::Int..10^8])()之类的类型应该已经修复了……我用不同的无限列表尝试了这个示例。使用
重复1
时没有空间泄漏,使用
[1,1..]
时有空间泄漏。也许是
enumFrom
和friends的算法造成的?@n.m.这是因为
repeat
产生的无限列表由于共享而占用了恒定的空间。如果您尝试
map id(repeat 1)
破坏共享,它将再次泄漏。虽然
last(map id(repeat 1))^seq^()
将泄漏,
last(map id(repeat())^seq^()
不会泄漏。然而,
last(map id(repeat Wtf))^seq^()
其中
data Wtf=Wtf
。(用backtick替换“^”)seq只要求它的左参数位于WHNF中。从逻辑上讲,我认为,如果要求
seq(last[1..10^9])x的结果是一个空间泄漏,那么要求
last[1..10^9]
结果的任何东西都是一个空间泄漏(事实并非如此)*很抱歉,我写的东西与上面讨论中的不同,这个降价陷阱阻止我使用反勾号。我尝试了与enumDeltaInteger相同的元素严格策略,但在ghci中没有任何效果。所以我猜ghci是在装傻。甚至可能是错误。如果它已经达到上限,它将不得不强制计算该值,因此我认为这不是一个可能的解释。我认为枚举或整数与此无关。问题是我不确定Haskell在任何情况下都应该如何工作,所以我很难报告一个bug。也许可以尝试使用-frewrite规则的ghci?
enumDeltaInteger :: Integer -> Integer -> [Integer]
enumDeltaInteger x d = x `seq` (x : enumDeltaInteger (x+d) d)
-- strict accumulator, so
--     head (drop 1000000 [1 .. ]
-- works
enumDeltaToInteger :: Integer -> Integer -> Integer -> [Integer]
enumDeltaToInteger x delta lim
  | delta >= 0 = up_list x delta lim
  | otherwise  = dn_list x delta lim

up_list :: Integer -> Integer -> Integer -> [Integer]
up_list x0 delta lim = go (x0 :: Integer)
                where
                    go x | x > lim   = []
                         | otherwise = x : go (x+delta)
let
  it_ax6 :: ()      
  it_ax6 =
    case last
           @ GHC.Integer.Type.Integer
           (GHC.Enum.enumFromTo
              @ GHC.Integer.Type.Integer
              GHC.Num.$fEnumInteger
              (GHC.Integer.smallInteger 1)
              (GHC.Real.^
                 @ GHC.Integer.Type.Integer
                 @ GHC.Integer.Type.Integer
                 GHC.Num.$fNumInteger
                 GHC.Real.$fIntegralInteger
                 (GHC.Integer.smallInteger 10)
                 (GHC.Integer.smallInteger 7)))
    of _ -> GHC.Unit.()
in
  GHC.Base.thenIO
    @ ()
    @ [()]
    (System.IO.print @ () GHC.Show.$fShow() it_ax6)
    (GHC.Base.returnIO
       @ [()] (GHC.Types.: @ () it_ax6 (GHC.Types.[] @ ())))
let {
  it_aKj :: GHC.Integer.Type.Integer
  [LclId,
   Unf=Unf{Src=<vanilla>, TopLvl=False, Arity=0, Value=False,
           ConLike=False, Cheap=False, Expandable=False,
           Guidance=IF_ARGS [] 170 0}]
  it_aKj =
    GHC.List.last
      @ GHC.Integer.Type.Integer
      (GHC.Enum.enumFromTo
         @ GHC.Integer.Type.Integer
         GHC.Num.$fEnumInteger
         (GHC.Integer.smallInteger 1)
         (GHC.Real.^
            @ GHC.Integer.Type.Integer
            @ GHC.Integer.Type.Integer
            GHC.Num.$fNumInteger
            GHC.Real.$fIntegralInteger
            (GHC.Integer.smallInteger 10)
            (GHC.Integer.smallInteger 7))) } in
GHC.Base.thenIO
  @ ()
  @ [()]
  (System.IO.print
     @ GHC.Integer.Type.Integer GHC.Num.$fShowInteger it_aKj)
  (GHC.Base.returnIO
     @ [()]
     (GHC.Types.:
        @ ()
        (it_aKj
         `cast` (UnsafeCo GHC.Integer.Type.Integer ()
                 :: GHC.Integer.Type.Integer ~ ()))
        (GHC.Types.[] @ ())))
let x = case last (enumFromTo 1 n) of _ -> ()