从 7.6.1开始,无论我使用还是作为索引类型,使用-O2
and-dsuppress-uniques
完成工作的函数在Main.main_$spoly_$wa
结构上(几乎)是相同的。由于核心又长又复杂,所以输出如下:int
Int64
diff
$ diff Int_core.dump-simpl Int64_core.dump-simpl
11,12c11,12
< (Data.Array.Base.STUArray s GHC.Types.Int GHC.Types.Int)
< (Main.C (Data.Array.Base.STUArray s GHC.Types.Int GHC.Types.Int))
---
> (Data.Array.Base.STUArray s GHC.Int.Int64 GHC.Types.Int)
> (Main.C (Data.Array.Base.STUArray s GHC.Int.Int64 GHC.Types.Int))
26,27c26,27
< (Data.Array.Base.STUArray s GHC.Types.Int GHC.Types.Int)
< (Main.C (Data.Array.Base.STUArray s GHC.Types.Int GHC.Types.Int)))
---
> (Data.Array.Base.STUArray s GHC.Int.Int64 GHC.Types.Int)
> (Main.C (Data.Array.Base.STUArray s GHC.Int.Int64 GHC.Types.Int)))
不同的索引类型,这些当然是不同的。
33,40d32
< l :: GHC.Types.Int
< [LclId]
< l = GHC.Types.I# sc } in
< let {
< u :: GHC.Types.Int
< [LclId]
< u = GHC.Types.I# sc1 } in
< let {
对于 index type Int
,GHC 会为越界索引产生更多信息错误,因为它需要有效索引的下限和上限。( 的默认实现在 .index
中没有被覆盖instance Ix Int64
。)
45,46c37
< GHC.Types.False ->
< case poly_$w$j5 (GHC.Types.I# a) l u of wild2 { };
---
> GHC.Types.False -> case GHC.Arr.hopelessIndexError of wild1 { };
不同的错误,indexError
与hopelessIndexError
. 以下差异也仅涉及索引错误。
49,50c40
< GHC.Types.False ->
< case poly_$w$j5 (GHC.Types.I# a) l u of wild2 { };
---
> GHC.Types.False -> case GHC.Arr.hopelessIndexError of wild2 { };
58c48
< case poly_$w$j4 y (GHC.Types.I# sc2) of wild3 { };
---
> case poly_$w$j3 y (GHC.Types.I# sc2) of wild4 { };
62c52
< case poly_$w$j4 y (GHC.Types.I# sc2) of wild5 { };
---
> case poly_$w$j3 y (GHC.Types.I# sc2) of wild5 { };
77,78c67
< GHC.Types.False ->
< case poly_$w$j3 (GHC.Types.I# a1) l u of wild6 { };
---
> GHC.Types.False -> case GHC.Arr.hopelessIndexError of wild6 { };
81,82c70
< GHC.Types.False ->
< case poly_$w$j3 (GHC.Types.I# a1) l u of wild7 { };
---
> GHC.Types.False -> case GHC.Arr.hopelessIndexError of wild7 { };
现在再次使用不同的索引类型:
110c98
< GHC.Types.Int
---
> GHC.Int.Int64
152c140
< s GHC.Types.Int GHC.Types.Int>)>)
---
> s GHC.Int.Int64 GHC.Types.Int>)>)
最后,0
得到1
了不同的顶级名称。
177,178c165,166
< 0 -> (# sc5, lvl5 #);
< 1 -> (# sc5, lvl6 #)
---
> 0 -> (# sc5, lvl #);
> 1 -> (# sc5, lvl1 #)
所以完成实际工作的整个代码是相同的。然而,一个会导致堆栈溢出(尽管只是,-K9M
足够 [-K8731K
在这里就足够了,-K8730K
不是]),而另一个则不会。
差异确实是由索引错误引起的。带有Int
索引的代码Int
在代码未分配的每个递归调用中分配两个装箱的 s Int64
,因为
Main.main_$spoly_$wa [Occ=LoopBreaker]
:: forall s.
GHC.Prim.Int#
-> GHC.Prim.Int#
-> GHC.Prim.Int#
-> GHC.Prim.MutableByteArray# s
-> (GHC.Prim.~#)
*
(Data.Array.Base.STUArray s GHC.Types.Int GHC.Types.Int)
(Main.C (Data.Array.Base.STUArray s GHC.Types.Int GHC.Types.Int))
-> GHC.Prim.Int#
-> GHC.Prim.State# s
-> (# GHC.Prim.State# s, GHC.Types.Int #)
携带对数组的两个引用。
这使用了更多的堆栈,并且这些装箱Int
的 s 必须被垃圾收集,这会导致更大的 GC 数字。此外,索引错误的 thunk 比hopelessIndexError
thunk 稍大。
现在,如果您通过以下方式帮助编译器
- 删除 newtype 包装器
- 使函数在数组中严格(通过 bang 模式或
data C a = C !a
)
或者其他一些方式,它可以生成更好的代码,在没有给定参数的堆栈溢出的情况下进行管理,因为在 worker 中只有一个Int
对数组的引用,因此不需要为边界分配装箱的 s。
请注意,即使在编译器的帮助下,此算法也会导致稍大的参数出现堆栈溢出。