ghc -O3 Main
)并运行。
注意:
Foo.hs
非常简化。如果您想了解为什么需要约束条件,可以在这里看到更多代码。如果我将代码放在单个文件中或进行许多其他微小的更改,GHC会直接内联调用plusFastCyc
。这在真实代码中不会发生,因为plusFastCyc
太大了,即使标记为INLINE
,GHC也无法内联。重点是要专门化对plusFastCyc
的调用,而不是内联它。plusFastCyc
在真实代码中的许多地方都被调用,因此即使我可以强制GHC这样做,复制如此大的函数也是不可取的。
感兴趣的代码是Foo.hs
中的plusFastCyc
,在此处再次复制:
{-# 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秒,以及使用forall
特化编译时需要约83秒的fcTest
。
core shows显示,对于vtTest
测试,加法代码被专门化为Unboxed
向量和Int
等类型,而对于fcTest
,使用通用向量代码。
在第10行,可以看到GHC确实写了一个专门化版本的plusFastCyc
,与第167行的通用版本相比。
专门化规则在第225行。我认为这条规则应该在第270行触发。(main6
调用iterate main8 y
,因此plusFastCyc
应该在main8
中进行专门化。)
我的目标是通过专门化plusFastCyc
,使fcTest
与vtTest
一样快。我找到了两种方法来实现这一点:
- 在
fcTest
中显式调用GHC.Exts
中的inline
。 - 删除
plusFastCyc
上的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
(以及here)警告,尽管在最小化示例之前我收到了很多match_co
警告。可以推测,“问题”是规则中的Factored m Int
约束;如果我对该约束进行更改,则fcTest
运行速度与vtTest
一样快。
我是否做了一些GHC不喜欢的事情?为什么GHC不会专门化plusFastCyc
,我该如何做呢?
更新
问题在GHC 7.8.2中仍存在,因此这个问题仍然是相关的。
m
,也就是M
进行专门化处理。这样做可以完成任务,但在实际程序中我无法为特定的幻象类型进行专门化处理,因为它们被实例化了。 - crockeea