具有约束的专业化
我有问题让GHC专门化一个类约束function。 我在这里有一个最小的例子: Foo.hs和Main.hs。 这两个文件编译(GHC 7.6.2, ghc -O3 Main
)并运行。
注意: Foo.hs
真的被剥夺了。 如果你想知道为什么需要约束,你可以在这里看到更多的代码。 如果我把代码放在一个单独的文件中,或者做了很多其他的小改动,GHC简单地把调用join到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 (tr)) => (FastCyc tr) -> (FastCyc tr) -> (FastCyc tr) plusFastCyc (PowBasis v1) (PowBasis v2) = PowBasis $ v1 + v2
Main.hs
文件有两个驱动程序:运行时间大约为3秒的fcTest
和运行时间大约为83秒的fcTest
,当使用fcTest
-O3进行编译时。
核心显示 ,对于vtTest
testing,附加代码专用于通过Int
等的fcTest
向量,而通用向量代码用于fcTest
。 在第10行,你可以看到GHC编写了plusFastCyc
的专用版本,与第167行的通用版本相比较。专业化的规则在第225行。我相信这条规则应该在第270行触发。( main6
调用iterate main8 y
,所以main8
是plusFastCyc
应该专用的地方。)
我的目标是通过专门的plusFastCyc
使fcTest
与fcTest
一样快。 我find了两种方法来做到这一点:
- 在
fcTest
显式调用fcTest
。 - 删除
Factored m Int
上的Factored m Int
约束。
选项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
(和这里 )警告,尽pipe在最小化这个例子之前我得到了许多match_co
警告。 据推测,这个“问题”是规则中的Factored m Int
约束。 如果我对该约束进行更改, fcTest
运行速度与vtTest
一样快。
我正在做什么GHC只是不喜欢? 为什么GHC不能专注于plusFastCyc
,我该怎么做呢?
UPDATE
这个问题在GHC 7.8.2中仍然存在,所以这个问题仍然是相关的。
GHC还提供了一个SPECIALIZE
types实例声明的选项。 我试着用Foo.hs
的(扩展)代码Foo.hs
,
instance (Num r, V.Vector vr, Factored mr) => Num (VT vmr) where {-# SPECIALIZE instance ( Factored m Int => Num (VT U.Vector m Int)) #-} VT x + VT y = VT $ V.zipWith (+) xy
但是,这个变化并没有达到预期的加速。 实现这种性能改进的是为typesVT U.Vector m Int
手动添加一个具有相同函数定义的专用实例,如下所示:
instance (Factored m Int) => Num (VT U.Vector m Int) where VT x + VT y = VT $ V.zipWith (+) xy
这需要在LANGUAGE
添加OverlappingInstances
和FlexibleInstances
。
有趣的是,在示例程序中,即使删除了每个SPECIALIZE
和INLINABLE
编译指示,使用重叠实例获得的加速仍然保留。