GHC中自动专业化的传递性

时间:2014-02-01 19:13:13

标签: haskell ghc

来自the docs的GHC 7.6:

  

[Y]你通常甚至不需要SPECIALIZE pragma。在编译模块M时,GHC的优化器(带-O)自动考虑在M中声明的每个顶级重载函数,并将其专门用于在M中调用它的不同类型。优化器还考虑每个导入的INLINABLE重载函数,并将其专门用于M中调用它的不同类型。

  

此外,给定函数f的SPECIALIZE编译指示,GHC将自动为f调用的任何类型类重载函数创建特殊化,如果它们与SPECIALIZE编译指示位于同一模块中,或者它们是否为INLINABLE;等等,过渡性地。

因此,GHC应该自动专门标记INLINABLE some/most/all(?)函数而不使用编译指示,如果我使用显式编译指示,则专门化是可传递的。我的问题是: 是自动 - 特殊化传递吗?

具体来说,这是一个小例子:

Main.hs:

import Data.Vector.Unboxed as U
import Foo

main =
    let y = Bar $ Qux $ U.replicate 11221184 0 :: Foo (Qux Int)
        (Bar (Qux ans)) = iterate (plus y) y !! 100
    in putStr $ show $ foldl1' (*) ans

Foo.hs:

module Foo (Qux(..), Foo(..), plus) where

import Data.Vector.Unboxed as U

newtype Qux r = Qux (Vector r)
-- GHC inlines `plus` if I remove the bangs or the Baz constructor
data Foo t = Bar !t
           | Baz !t

instance (Num r, Unbox r) => Num (Qux r) where
    {-# INLINABLE (+) #-}
    (Qux x) + (Qux y) = Qux $ U.zipWith (+) x y

{-# INLINABLE plus #-}
plus :: (Num t) => (Foo t) -> (Foo t) -> (Foo t)
plus (Bar v1) (Bar v2) = Bar $ v1 + v2

GHC专门调用plus,但在(+) Qux实例中 不会专门化Num,这会影响性能。

然而,一个明确的编译指示

{-# SPECIALIZE plus :: Foo (Qux Int) -> Foo (Qux Int) -> Foo (Qux Int) #-}
如文档所示,

会产生传递特化,因此(+)是专用的,代码快30倍(都使用-O2编译)。这是预期的行为吗?我是否应该只期望(+)具有明确的编译指示传递?


更新

7.8.2的文档没有改变,行为也是一样的,所以这个问题仍然有用。

1 个答案:

答案 0 :(得分:3)

简答:

正如我所理解的那样,问题的关键点如下:

  
      
  • “是自动专业化传递?”
  •   
  • 我是否应该只期望(+)使用显式的pragma进行传递?
  •   
  • (显然是有意的)这是GHC的错误吗?它与文档不一致吗?
  •   

AFAIK,答案是否定的,大部分是肯定的,但还有其他方法,而且不是。

代码内联和类型应用程序专门化是速度(执行时间)和代码大小之间的权衡。默认级别获得一些加速,而不会膨胀代码。选择更详尽的级别由程序员通过SPECIALISE pragma自行决定。

说明:

  

优化器还会考虑每个导入的INLINABLE重载函数,并将其专门用于在M中调用它的不同类型。

假设f是一个函数,其类型包含受类型类a约束的类型变量C a。 GHC默认专门针对类型应用程序f(用a代替t),如果{(1)}在(a)的源代码中使用该类型应用程序调用在同一模块中运行,或者(b)如果f标记为f,则{strong>从INLINABLE 导入 f的任何其他模块。因此,自动专精化不具有传递性,它仅触及在B的源代码中为导入和调用的INLINABLE函数。

在您的示例中,如果您重写A的实例,如下所示:

Num
    {li> instance (Num r, Unbox r) => Num (Qux r) where (+) = quxAdd quxAdd (Qux x) (Qux y) = Qux $ U.zipWith (+) x y 未由quxAdd专门导入。 Main导入Main的实例字典,此字典在Num (Qux Int)的记录中包含quxAdd。但是,虽然导入了字典,但字典中使用的内容却不是。
  • (+)未调用plus,它使用quxAdd实例字典中(+)记录存储的函数。此字典由编译器在调用站点(Num t)中设置。