问题描述
当我运行类似的内容时:
When I run something like:
Prelude> cycle "ab"
我可以看到"ab"的无限印刷.要停止它,我只需使用 + .而且有效.
I can see an infinite printing of "ab". To stop it I just use +. And it works.
当我跑步时:
Prelude Data.List> nub $ cycle "ab"
我无法阻止它.
问题:
- 为什么会这样?
- 如何停止此操作?
更新:
Ubuntu: version 18.10
GHCi: version 8.2.2
推荐答案
好问题!但是,由于如何在GHCI中中止执行?已经着重于第二部分,我们在此不再赘述.相反,让我们专注于第一个.
Great question! However, since How to abort execution in GHCI? already focuses on your second part, let's not repeat that here. Instead, let's focus on the first.
GHC积极优化循环.如果没有分配这甚至是一个已知的错误:
GHC optimizes loops aggressively. It optimizes them even further if there is no allocation that it's even a known bug:
如果遇到此问题,则可能需要使用 -fno-omit-yields
(请参见 -f *:与平台无关的标志 ).此标志可确保在每个函数入口点插入屈服点(以牺牲一些性能为代价).
If you are hit by this, you may want to compile the affected module with -fno-omit-yields
(see -f*: platform-independent flags). This flag ensures that yield points are inserted at every function entrypoint (at the expense of a bit of performance).
如果我们检查 -fomit-yields
,我们发现:
If we check -fomit-yields
, we find:
默认:启用屈服点
告诉GHC在不执行分配时省略堆检查.虽然这可以将二进制文件大小提高5%左右,但这也意味着 在紧密的非分配循环中运行的线程不会被抢占 及时地. 如果始终能够打断很重要 这样的线程,您应该关闭此优化.还考虑 如果您关闭了此优化功能,则重新编译所有库 需要保证可中断性. [强调我的]
Tells GHC to omit heap checks when no allocation is being performed. While this improves binary sizes by about 5%, it also means that threads run in tight non-allocating loops will not get preempted in a timely fashion. If it is important to always be able to interrupt such threads, you should turn this optimization off. Consider also recompiling all libraries with this optimization turned off, if you need to guarantee interruptibility. [emphasis mine]
nub $ cycle "ab"
是一个紧密的非分配循环,尽管last $ repeat 1
是一个更明显的非分配示例.
nub $ cycle "ab"
is a tight, non-allocating loop, although last $ repeat 1
is an even more obvious non-allocating example.
启用屈服点"具有误导性:默认情况下启用-fomit-yields
.由于标准库是使用-fomit-yields
编译的,因此标准库中所有导致紧密,非分配循环的函数可能表明GHCi中的行为,因为您无需重新编译它们.
The "yield points enabled" is misleading: -fomit-yields
is enabled by default. As the standard library is compiled with -fomit-yields
, all functions in the standard library that lead to tight, non-allocating loops may show that behaviour in GHCi, as you never recompile them.
我们可以使用以下程序进行验证:
We can verify that with the following program:
-- Test.hs
myLast :: [a] -> Maybe a
myLast [x] = Just x
myLast (_:xs) = myLast xs
myLast _ = Nothing
main = print $ myLast $ repeat 1
如果我们在GHCi中运行它,则可以使用退出它,而无需事先编译:
We can use to quit it if we run it in GHCi without compiling beforehand:
$ ghci Test.hs
[1 of 1] Compiling Main ( Test.hs, interpreted )
Ok, one module loaded.
*Main> :main <pressing C-c after a while>
Interrupted.
如果我们编译它,然后在GHCi中重新运行它,它将挂起:
If we compile it and then rerun it in GHCi, it will hang:
$ ghc Test.hs
[1 of 1] Compiling Main ( Test.hs, Test.o )
Linking Test.exe ...
$ ghci Test.hs
Ok, one module loaded.
*Main> :main
<hangs indefinitely>
请注意,如果您不使用Windows,则需要-dynamic
,否则GHCi将重新编译源文件.但是,如果使用-fno-omit-yield
,我们突然可以再次退出(在Windows中).
Note that you need -dynamic
if you don't use Windows, as otherwise GHCi will recompile the source file. However, if we use -fno-omit-yield
, we suddenly can quit again (in Windows).
我们可以使用另一个小片段再次进行验证:
We can verify that again with another small snippet:
Prelude> last xs = case xs of [x] -> x ; (_:ys) -> last ys
Prelude> last $ repeat 1
^CInterrupted
由于ghci
不使用任何优化,因此它也不使用-fomit-yield
(因此已启用-fno-omit-yield
).我们新的last
变体与Prelude.last
的行为不同,因为它不是用fomit-yield
编译的.
As ghci
doesn't use any optimizations, it also doesn't use -fomit-yield
(and therefore has -fno-omit-yield
enabled). Our new variant of last
doesn't yield the same behaviour as Prelude.last
as it isn't compiled with fomit-yield
.
现在我们知道发生这种情况的原因了,我们知道随着标准库使用-fomit-yield
编译,我们将在整个标准库中遇到这种行为.
Now that we know why this happens, we know that we will experience that behaviour throughout the standard library, as the standard library is compiled with -fomit-yield
.
这篇关于如何停止GHCi中的无限评估?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!