约束专业化

时间:2014-01-12 05:39:40

标签: haskell ghc

我遇到了让GHC专门化具有类约束的函数的问题。我在这里有一个最小的问题示例:Foo.hsMain.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使plusFastCycfcTest一样快。我找到了两种方法:

  1. vtTest {/ 1}}中的plusFastCyc透露来电inline
  2. 删除GHC.Exts上的fcTest约束。
  3. 选项1不能令人满意,因为在实际代码库中Factored m Int是一个经常使用的操作和一个非常大函数,因此不应该在每次使用时都内联。相反,GHC应该调用plusFastCyc的专用版本。选项2实际上不是一个选项,因为我需要实际代码中的约束。

    我尝试过使用(而不是使用)plusFastCycplusFastCycINLINE的各种选项,但似乎没有任何效果。 (编辑:我可能已经删除了太多INLINABLE以使我的示例变小,因此SPECIALIZE可能会导致函数内联。这不会发生在我的身上实际代码,因为plusFastCyc太大了。)在这个特定示例中,我没有收到任何match_co: needs more casesRULE: LHS too complicated to desugar(和here)警告,但我收到了很多在最小化示例之前发出INLINE警告。据推测,“问题”是规则中的plusFastCyc约束;如果我对该约束进行了更改,match_co的运行速度与Factored m Int一样快。

    我做的事GHC就是不喜欢?为什么GHC不会专注于fcTest,我该如何制作呢?

    更新

    问题仍然存在于GHC 7.8.2中,所以这个问题仍然存在。

1 个答案:

答案 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中添加FlexibleInstancesLANGUAGE

有趣的是,在示例程序中,即使您删除了每个SPECIALIZEINLINABLE pragma,使用重叠实例获得的加速也会保留。