我遇到了让GHC专门化具有类约束的函数的问题。我在这里有一个最小的问题示例:Foo.hs和Main.hs。这两个文件编译(GHC 7.6.2,ghc -O3 Main
)并运行。
注意:
Foo.hs
真的被剥夺了。如果您想了解为什么需要约束,您可以看到更多代码here。如果我将代码放在单个文件中或进行许多其他微小更改,GHC只会简单地调用plusFastCyc
。这不会发生在实际代码中,因为plusFastCyc
对于GHC来说太大而无法内联,即使标记为INLINE
也是如此。关键是要专门化对plusFastCyc
的调用,而不是内联它。 plusFastCyc
在真实代码的许多地方被调用,所以即使我可以强迫GHC这样做,也不可能复制这么大的函数。
感兴趣的代码是plusFastCyc
中的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
Main.hs
文件有两个驱动程序:vtTest
,运行时间约为3秒; fcTest
,运行时间约为83秒,使用{{1专业化。
forall
测试的core shows,加法代码专门用于vtTest
s上的Unboxed
个向量,而通用向量代码用于{ {1}}。
在第10行,您可以看到GHC确实编写了Int
的专用版本,与第167行的通用版本相比。
专业化的规则在第225行。我相信这条规则应该在第270行触发。(fcTest
调用plusFastCyc
,因此main6
应该是iterate main8 y
专用的地方。 )
我的目标是通过专业化main8
使plusFastCyc
与fcTest
一样快。我找到了两种方法:
vtTest
{/ 1}}中的plusFastCyc
透露来电inline
GHC.Exts
上的fcTest
约束。选项1不能令人满意,因为在实际代码库中Factored m Int
是一个经常使用的操作和一个非常大函数,因此不应该在每次使用时都内联。相反,GHC应该调用plusFastCyc
的专用版本。选项2实际上不是一个选项,因为我需要实际代码中的约束。
我尝试过使用(而不是使用)plusFastCyc
,plusFastCyc
和INLINE
的各种选项,但似乎没有任何效果。 (编辑:我可能已经删除了太多INLINABLE
以使我的示例变小,因此SPECIALIZE
可能会导致函数内联。这不会发生在我的身上实际代码,因为plusFastCyc
太大了。)在这个特定示例中,我没有收到任何match_co: needs more cases
或RULE: LHS too complicated to desugar
(和here)警告,但我收到了很多在最小化示例之前发出INLINE
警告。据推测,“问题”是规则中的plusFastCyc
约束;如果我对该约束进行了更改,match_co
的运行速度与Factored m Int
一样快。
我做的事GHC就是不喜欢?为什么GHC不会专注于fcTest
,我该如何制作呢?
更新
问题仍然存在于GHC 7.8.2中,所以这个问题仍然存在。
答案 0 :(得分:4)
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
但是,这种变化并没有达到预期的加速。实现该性能改进的是手动为具有相同功能定义的类型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
这需要在OverlappingInstances
中添加FlexibleInstances
和LANGUAGE
。
有趣的是,在示例程序中,即使您删除了每个SPECIALIZE
和INLINABLE
pragma,使用重叠实例获得的加速也会保留。