具有约束的专业化

我有问题让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进行编译时。

核心显示 ,对于vtTesttesting,附加代码专用于通过Int等的fcTest向量,而通用向量代码用于fcTest 。 在第10行,你可以看到GHC编写了plusFastCyc的专用版本,与第167行的通用版本相比较。专业化的规则在第225行。我相信这条规则应该在第270行触发。( main6调用iterate main8 y ,所以main8plusFastCyc应该专用的地方。)

我的目标是通过专门的plusFastCyc使fcTestfcTest一样快。 我find了两种方法来做到这一点:

  1. fcTest显式调用fcTest
  2. 删除Factored m Int上的Factored m Int约束。

选项1并不令人满意,因为在实际的代码库中, plusFastCyc是一个经常使用的操作和一个非常大的函数,所以不应该在每次使用时都内联。 相反,GHC应该调用plusFastCyc的专用版本。 选项2并不是一个真正的选项,因为我需要在真正的代码中的约束。

我尝试了使用(而不是使用) INLINEINLINABLESPECIALIZE的各种选项,但似乎没有任何工作。 ( 编辑 :我可能已经剥离了太多的plusFastCyc使我的例子很小,所以INLINE可能会导致函数被内联。这不会发生在我的真实代码,因为plusFastCyc是如此之大)。在这个特定的例子中,我没有得到任何match_co: needs more casesRULE: 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还提供了一个SPECIALIZEtypes实例声明的选项。 我试着用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添加OverlappingInstancesFlexibleInstances

有趣的是,在示例程序中,即使删除了每个SPECIALIZEINLINABLE编译指示,使用重叠实例获得的加速仍然保留。