<<loop>>错误"work"详细怎么说?
How does the <<loop>> error "work" in detail?
我正在开发这个工具,用户可以在其中定义并包含在[配置文件 |内容文本文件 | etc] 他们自己的 "templates" (比如小胡子等),这些可以引用其他人所以他们可以引发循环。就在我正要创建一个 "max-loops" 设置时,我用 runghc 意识到该程序在一段时间后退出并带有 <<loop>>
的告别消息。这对我来说实际上已经足够好了,但引发了一些思考:
GHC 或运行时如何实际检测到它陷入了循环,以及它如何区分需要的 long-运行 操作和意外的无限循环?停止问题仍然是我上次检查的问题..
可以为编译器或运行时自定义设置的任何(时间或迭代)限制?
这是 runghc
-only 还是它存在于所有最终编译输出中?
在构建版本禁用此明显的内置循环检测时,是否会设置任何 -o
(优化)标志?
所有的东西我当然可以用困难的方式弄清楚,但谁知道也许有人已经更详细地研究过这个..(很难 google/ddg 因为 "haskell" "<<loop>>"
因为他们去掉了角度括号,然后显示 "how to loop in Haskell" 等的结果。)
这是在 GHC 中实现的 STG 运行时的简单 "improvement"。我会分享我的理解,但 GHC 专家可能会提供更有用和准确的信息。
GHC 在进行了多次优化后编译为一种称为 Core 的中间语言。您可以使用 ghc -ddump-simpl ...
查看它
非常粗略地说,在 Core 中,未计算的绑定(如 let x = 1+y+x in f x
)会创建一个 thunk。在某处分配了一些内存来表示闭包,并使 x
指向它。
当(并且如果)x
被 f
强制执行时,thunk 被评估。这是改进:在计算开始之前,x
的 thunk 被一个名为 BLACKHOLE
的特殊值覆盖。在 x
被评估(对 WHNF)之后,黑洞再次被实际值覆盖(因此我们不会重新计算它,例如 f x = x+x
)。
如果黑洞曾经被强制触发,<<loop>>
就会被触发。这实际上是一个 IO 异常(那些也可以在纯代码中引发,所以这很好)。
示例:
let x = 1+x in 2*x -- <<loop>>
let g x = g (x+1) in g 0 -- diverges
let h x = h (10-x) in h 0 -- diverges, even if h 0 -> h 10 -> h 0 -> ...
let g0 = g10 ; g10 = g0 in g0 -- <<loop>>
请注意,h 0
的每次调用都被认为是一个不同的 thunk,因此没有黑洞被强制在那里。
棘手的部分是了解哪些 thunk 实际上是在 Core 中创建的并不是完全微不足道的,因为 GHC 可以在发出 Core 之前执行多项优化。因此,我们应该将 <<loop>>
视为奖励,而不是 GHC 的给定/硬性保证。未来的新优化可能会用实际的非终止替换一些 <<loop>>
。
如果你想google一些东西,"GHC, blackhole, STG"应该是很好的关键字。
我正在开发这个工具,用户可以在其中定义并包含在[配置文件 |内容文本文件 | etc] 他们自己的 "templates" (比如小胡子等),这些可以引用其他人所以他们可以引发循环。就在我正要创建一个 "max-loops" 设置时,我用 runghc 意识到该程序在一段时间后退出并带有 <<loop>>
的告别消息。这对我来说实际上已经足够好了,但引发了一些思考:
GHC 或运行时如何实际检测到它陷入了循环,以及它如何区分需要的 long-运行 操作和意外的无限循环?停止问题仍然是我上次检查的问题..
可以为编译器或运行时自定义设置的任何(时间或迭代)限制?
这是
runghc
-only 还是它存在于所有最终编译输出中?在构建版本禁用此明显的内置循环检测时,是否会设置任何
-o
(优化)标志?
所有的东西我当然可以用困难的方式弄清楚,但谁知道也许有人已经更详细地研究过这个..(很难 google/ddg 因为 "haskell" "<<loop>>"
因为他们去掉了角度括号,然后显示 "how to loop in Haskell" 等的结果。)
这是在 GHC 中实现的 STG 运行时的简单 "improvement"。我会分享我的理解,但 GHC 专家可能会提供更有用和准确的信息。
GHC 在进行了多次优化后编译为一种称为 Core 的中间语言。您可以使用 ghc -ddump-simpl ...
非常粗略地说,在 Core 中,未计算的绑定(如 let x = 1+y+x in f x
)会创建一个 thunk。在某处分配了一些内存来表示闭包,并使 x
指向它。
当(并且如果)x
被 f
强制执行时,thunk 被评估。这是改进:在计算开始之前,x
的 thunk 被一个名为 BLACKHOLE
的特殊值覆盖。在 x
被评估(对 WHNF)之后,黑洞再次被实际值覆盖(因此我们不会重新计算它,例如 f x = x+x
)。
如果黑洞曾经被强制触发,<<loop>>
就会被触发。这实际上是一个 IO 异常(那些也可以在纯代码中引发,所以这很好)。
示例:
let x = 1+x in 2*x -- <<loop>>
let g x = g (x+1) in g 0 -- diverges
let h x = h (10-x) in h 0 -- diverges, even if h 0 -> h 10 -> h 0 -> ...
let g0 = g10 ; g10 = g0 in g0 -- <<loop>>
请注意,h 0
的每次调用都被认为是一个不同的 thunk,因此没有黑洞被强制在那里。
棘手的部分是了解哪些 thunk 实际上是在 Core 中创建的并不是完全微不足道的,因为 GHC 可以在发出 Core 之前执行多项优化。因此,我们应该将 <<loop>>
视为奖励,而不是 GHC 的给定/硬性保证。未来的新优化可能会用实际的非终止替换一些 <<loop>>
。
如果你想google一些东西,"GHC, blackhole, STG"应该是很好的关键字。