您好,我遇到过ghc优化标志的有线行为.优化标志似乎改变了评估方式.综上所述,
primes
并isPrime
通过引用彼此定义的代码.ghc -O3
,但我不能runhaskell
用来得到结果.这花费了太多时间.ghc -O1
,结果立即出现-O3
,但编译的可执行文件ghc -O0
无法在一分钟内计算结果.Debug.Trace.trace
发现,primes
每次isPrime
调用时都会从它的开始进行评估.primes
,并isPrime
到另一个文件Prime.hs
.在主文件中,我导入了Prime库.不幸的是,编译的可执行文件ghc -O3
不会在一分钟内计算结果.这是描述.请参阅以下代码.
main :: IO ()
main = print $ length $ filter isPrime [100000..1000000]
primes :: Integral a => [a]
primes = 2 : filter isPrime [3,5..]
isPrime :: Integral a => a -> Bool
isPrime n = n > 1 && foldr (\p r -> p * p > n || (n `mod` p /= 0 && r)) True primes
Run Code Online (Sandbox Code Playgroud)
当我编译代码时ghc -O3
,可执行文件68906
在2秒内计算出正确的结果.
$ ghc -O3 test.hs
[1 of 1] Compiling Main ( test.hs, test.o )
Linking test ...
$ time ./test
68906
./test 1.24s user 0.02s system 79% cpu 1.574 total
Run Code Online (Sandbox Code Playgroud)
但是,当我使用时-O0
,我无法在一分钟内得到结果.请务必提前删除生成的文件.
$ rm -f ./test ./test.o ./test.hi
$ ghc -O0 test.hs
[1 of 1] Compiling Main ( test.hs, test.o )
Linking test ...
$ time ./test
^C
./test 64.34s user 0.94s system 94% cpu 1:08.90 total
Run Code Online (Sandbox Code Playgroud)
我流产了 国旗的-O1
效果与之相同-O3
.
所以让我们深入调查.我用过Debug.Trace.trace
.我追溯了这个论点isPrime
.
import Debug.Trace
main :: IO ()
main = print $ length $ filter isPrime [10..30]
primes :: (Show a, Integral a) => [a]
primes = 2 : filter isPrime [3,5..]
isPrime :: (Show a, Integral a) => a -> Bool
isPrime n = trace (show n) $ n > 1 && foldr (\p r -> p * p > n || (n `mod` p /= 0 && r)) True primes
Run Code Online (Sandbox Code Playgroud)
当优化标志是-O3
(或-O1
)时,输出如下.
10
11
3
5
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
7
30
6
Run Code Online (Sandbox Code Playgroud)
这个结果是合理的(注意最后一行打印素数的数量; 11,13,17,19,23,29).
这是-O0
(或runhaskell
)的结果
10
11
3
5
3
12
13
3
5
3
14
15
3
16
17
3
5
3
18
19
3
5
3
20
21
3
22
23
3
5
3
24
25
3
5
3
26
27
3
28
29
3
5
3
7
3
30
6
Run Code Online (Sandbox Code Playgroud)
这个结果很有趣.2已安排在头部primes
.如果isPrime
反复检查3和5 .当isPrime 11
被调用时,检查3是否isPrime 3
再次调用素数,并且还检查5 .同样,对于几乎每个奇数,isPrime 3
并且isPrime 5
一次又一次地被调用.
因此,我认为,当我使用时-O0
,primes
并不是[2]
每次isPrime
调用时都进行缓存和构造.所以第一个问题是为什么-O0
并-O1
改变评估的行为.
这是另一个问题.好的,好吧,但我很少使用-O0
旗帜.在大多数情况下,我使用-O2
或-O3
优化标志,所以我认为上述问题不会出现在许多用例中.
但当我将代码移动到另一个文件时,问题再次出现.我刚搬到primes
并isPrime
以Prime.hs.
test.hs:
import Prime
main :: IO ()
main = print $ length $ filter isPrime [100000..1000000]
Run Code Online (Sandbox Code Playgroud)
Prime.hs:
module Prime where
primes :: Integral a => [a]
primes = 2 : filter isPrime [3,5..]
isPrime :: Integral a => a -> Bool
isPrime n = n > 1 && foldr (\p r -> p * p > n || (n `mod` p /= 0 && r)) True primes
Run Code Online (Sandbox Code Playgroud)
在这个时候,我无法通过-O1
旗帜,甚至-O3
旗帜获得结果.
$ ghc -O3 test.hs
[1 of 2] Compiling Prime ( Prime.hs, Prime.o )
[2 of 2] Compiling Main ( test.hs, test.o )
Linking test ...
$ time ./test
^C
./test 62.41s user 0.88s system 92% cpu 1:08.23 total
Run Code Online (Sandbox Code Playgroud)
嗯,我再次流产了.我不知道这种方式是否会对结果产生影响,我-O3
事先预先编译了Prime.hs ,但是徒劳无功.我特此使用Debug.Trace.trace
,我一次又一次地看到2和3的-O3
旗帜.总之,由于评价方式发生变化时,我无法创建总理库primes
,并isPrime
移动到一个模块(这让我吃惊)和-O3
不使其工作.
所以第二个问题是,尽管有-O3
标志,为什么模块中的东西被评估为由-O0
flag 编译?
我终于厌倦了调查这种有线行为.我总结说我不应该在模块中使用交叉引用的定义.我放弃了创建我的Prime库并开始使用 Data.Numbers.Primes
.
提前致谢.
Car*_*arl 10
这里发生的是以下签名:
primes :: Integral a => [a]
Run Code Online (Sandbox Code Playgroud)
类型类可以防止primes
被天真地记忆.primes :: [Int]
是不一样的primes :: [Integer]
.并且不能共享任何计算,因为GHC不能假设所有实例Num
遵循相同的逻辑.因此,每次使用primes
最终都会重新计算所选类型的列表.
但是当你启用优化时,GHC会变得更加智能.当唯一使用的primes
是与定义相同的模块时,GHC可以将其优化为它所用的具体类型.然后在列表的使用中共享计算.
但它只在模块边界内执行此操作.单独的模块编译意味着如果primes
导出,它不能专门用于具体类型 - GHC永远不知道它将编译的下一个模块是否可能primes
在不同的类型中使用.
解决此问题的最简单方法是提供primes
具体类型.然后即使天真地使用它也会备忘.