我遇到了让GHC专门化一个具有类约束的函数的问题.我有我的问题就在这里的一个小例子:Foo.hs和 Main.hs.这两个文件编译(GHC 7.6.2,ghc -O3 Main)并运行.
注意:
Foo.hs真的被剥离了.如果您想了解为什么需要约束,可以在这里看到更多代码.如果我将代码放在一个文件中或进行许多其他微小的更改,GHC只是简单地将调用内联到plusFastCyc.这不会发生在实际代码中,因为plusFastCycGHC内联太大,即使在标记时也是如此INLINE.关键是要专门调用plusFastCyc,而不是内联它.plusFastCyc在真实代码的许多地方被调用,所以即使我可以强迫GHC这样做,也不可能复制这么大的功能.
感兴趣的代码是plusFastCycin Foo.hs,在这里转载:
{-# INLINEABLE plusFastCyc #-}
{-# SPECIALIZE plusFastCyc ::
forall m . (Factored m Int) =>
(FastCyc (VT U.Vector m) Int) ->
(FastCyc (VT U.Vector m) Int) ->
(FastCyc (VT U.Vector m) Int) #-}
-- Although the next specialization makes `fcTest` fast,
-- it isn't useful to me in my real program because the phantom type M is reified
-- {-# SPECIALIZE plusFastCyc ::
-- FastCyc (VT U.Vector M) Int ->
-- FastCyc (VT U.Vector M) Int ->
-- FastCyc (VT U.Vector M) Int #-}
plusFastCyc :: (Num (t r)) => (FastCyc t r) -> (FastCyc t r) -> (FastCyc t r)
plusFastCyc (PowBasis v1) (PowBasis v2) = PowBasis $ v1 + v2
Run Code Online (Sandbox Code Playgroud)
该Main.hs文件有两个驱动程序:vtTest运行时间约为3秒,fcTest使用forall'd specialization 时使用-O3编译时运行时间约为83秒.
的核心显示了,对于vtTest测试,添加代码被专用于Unboxed在载体IntS,等,而通用向量代码是用于fcTest.在第10行,您可以看到GHC确实写了一个专门版本plusFastCyc,与第167行的通用版本相比.专业化的规则在第225行.我相信这条规则应该在第270行开始.(main6调用iterate main8 y,所以main8是哪里plusFastCyc应该专门.)
我的目标是通过专业化来提高fcTest速度.我找到了两种方法:vtTestplusFastCyc
inline从GHC.Exts在fcTest.Factored m Int约束plusFastCyc.选项1是不能令人满意的,因为在实际的代码库中plusFastCyc是一个经常使用的操作和一个非常大的函数,所以它不应该在每次使用时都内联.相反,GHC应该调用一个专门的版本plusFastCyc.选项2实际上不是一个选项,因为我需要实际代码中的约束.
我已经尝试了多种选择使用(而不是使用)INLINE,INLINABLE以及SPECIALIZE,但似乎没有任何工作.(编辑:我可能已经剥离了太多plusFastCyc让我的例子变小,所以INLINE可能导致函数被内联.这不会发生在我的真实代码中,因为plusFastCyc它太大了.)在这个特殊的例子中,我不是获得任何match_co: needs more cases或RULE: LHS too complicated to desugar(和此处)警告,尽管match_co在最小化示例之前我收到了很多警告.据推测,"问题"是Factored m Int规则中的约束; 如果我对该约束进行更改,则fcTest运行速度最快vtTest.
我在做什么GHC只是不喜欢?为什么GHC不会专攻plusFastCyc,我该怎么做呢?
UPDATE
这个问题在GHC 7.8.2中仍然存在,所以这个问题仍然存在.
GHC还为SPECIALIZE类型类实例声明提供了一个选项。我使用的(扩展)代码尝试了此操作,方法Foo.hs如下:
instance (Num r, V.Vector v r, Factored m r) => Num (VT v m r) where
{-# SPECIALIZE instance ( Factored m Int => Num (VT U.Vector m Int)) #-}
VT x + VT y = VT $ V.zipWith (+) x y
Run Code Online (Sandbox Code Playgroud)
但是,此更改未实现所需的加速。确实实现了性能改进,是为具有相同功能定义的类型手动添加了一个专用实例VT U.Vector m Int,如下所示:
instance (Factored m Int) => Num (VT U.Vector m Int) where
VT x + VT y = VT $ V.zipWith (+) x y
Run Code Online (Sandbox Code Playgroud)
这就需要增加OverlappingInstances和FlexibleInstances在 LANGUAGE。
有趣的是,在示例程序中,即使您删除了每个SPECIALIZE和INLINABLE杂注,重叠实例所获得的加速仍然保持。