具有约束的专业化
我有问题让GHC专门化一个带有类限制的函数。 我在这里有一个我的问题的最小例子:Foo.hs和Main.hs. 这两个文件编译(GHC 7.6.2, ghc -O3 Main
)并运行。
注意: Foo.hs
真的被剥夺了。 如果你想知道为什么需要约束,你可以在这里看到更多的代码。 如果我将代码放在单个文件中或做出许多其他较小的更改,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秒当使用与-O3编译运行forall
“d专业化。
核心显示,对于vtTest
测试,添加代码被专用于Unboxed
在载体Int
S,等,而通用向量代码是用于fcTest
。 在第10行,你可以看到GHC确实编写了plusFastCyc
的专用版本,与第167行的通用版本相比较。专业化的规则在第225行。我相信这条规则应该在第270行触发。( main6
调用iterate main8 y
,所以main8
是plusFastCyc
应该专用的地方。)
我的目标是让fcTest
快vtTest
由专业plusFastCyc
。 我找到了两种方法来做到这一点:
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: needs more cases
(和这里)警告,尽管在最小化示例之前我得到了很多match_co
警告。 据推测,“问题”是规则中的Factored m Int
约束; 如果我对该约束进行更改,则fcTest
运行速度与vtTest
一样快。
我正在做什么GHC只是不喜欢? 为什么GHC不会专注于plusFastCyc
,我plusFastCyc
到这一点?
UPDATE
这个问题在GHC 7.8.2中仍然存在,所以这个问题仍然是相关的。
GHC还提供了一个SPECIALIZE
类型实例声明的选项。 我用Foo.hs
的(扩展)代码尝试了这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
这需要在LANGUAGE
添加OverlappingInstances
和FlexibleInstances
。
有趣的是,在示例程序中,即使删除了每个SPECIALIZE
和INLINABLE
注,使用重叠实例获得的加速仍然保留。