GHC专业化

从GHC 7.6文档:

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

此外,给定一个函数f的SPECIALIZE pragma,如果它们与SPECIALIZE pragma位于同一个模块中,或者它们是INLINABLE,GHC将自动为f调用的任何类重载函数创建专门化; 等等。

因此,GHC应该自动专门化一些标记为INLINABLE一些/ 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 (+) Qux Num实例中的性能。

但是,一个明确的杂注

{-# SPECIALIZE plus :: Foo (Qux Int) -> Foo (Qux Int) -> Foo (Qux Int) #-}

如文档所示,导致传递特化,所以(+)是专用的,代码速度提高了30倍(均使用-O2编译)。 这是预期的行为? 我是否应该只希望(+)通过一个明确的附注来传递专门知识?


UPDATE

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


简短的答案:

据我了解,问题的关键点如下:

  • “是自动专业化传递?”
  • 我是否应该只希望(+)通过一个明确的附注来传递专门知识?
  • (显然打算)这是GHC的错误? 它与文档不一致吗?
  • AFAIK,答案是否定的,大部分是肯定的,但还有其他方法,并且没有。

    代码内联和类型应用程序专业化是速度(执行时间)和代码大小之间的折中。 默认级别可以获得一些加速而不会膨胀代码。 程序员可以通过SPECIALISE pragma来选择更详尽的级别。

    说明:

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

    假设f是一个函数,其类型包括类型变量a由类型约束类C a 。 如果在(a)同一模块中的任何函数的源代码中使用该类型应用程序调用f ,或者(b)如果f标记为INLINABLE ,则GHC默认将f指定为类型应用程序(用a代替t )然后是从B 导入 f任何其他模块。 因此,自动专门化不是传递式的,它只涉及A的源代码中导入和调用的INLINABLE函数。

    在你的例子中,如果你重写了Num的实例,如下所示:

    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的实例字典中的(+)记录存储的函数。 该字典由编译器在调用站点( Main )设置。
  • 链接地址: http://www.djcxy.com/p/7517.html

    上一篇: Specialization in GHC

    下一篇: How are Haskell programs compiled and executed internally?