GHC中自动专业化的及物性


392

从GHC 7.6 的文档中:

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

此外,给定函数f的SPECIALIZE编译指示,GHC将自动为f调用的任何类型类重载函数创建专用化,如果它们与SPECIALIZE编译指示在同一模块中,或者它们是不可插入的;等等。

因此,GHC应该自动对标记为没有杂用语的某些/大多数/全部(?)函数进行特殊化处理,如果我使用显式的杂用语,则该特殊化是可传递的。我的问题是:自动专业化是可传递的吗?INLINABLE

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

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的文档未更改,其行为相同,因此此问题仍然相关。


33
我不知道答案,但听起来可能与以下问题有关:ghc.haskell.org/trac/ghc/ticket/5928如果您认为可能与5928有关,可能值得开张新票或在其中添加您的信息
jberryman 2014年

6
@jberryman似乎有是机票和我的问题之间有两点不同:1)在票,相当于plus没有标记为可以内联和2)simonpj表示,有一些内联回事票代码,而是从核心我的示例显示没有内联函数(特别是,我无法摆脱第二个Foo构造函数,否则就无法使用GHC内联的东西)。
crockeea 2014年

5
啊好吧。当您定义时plus (Bar v1) = \(Bar v2)-> Bar $ v1 + v2,会发生什么情况,以便在呼叫站点完全应用LHS?它会内联,然后开始专业化吗?
jberryman 2014年

3
@jberryman有趣,您应该问。我一直走在这个问题的路上,这个问题导致了这份追踪报告plus由于这些链接,我最初曾要求完全应用,但实际上我的专业化程度较低:对的调用plus也不是专门的。我对此没有任何解释,但打算将其留给另一个问题,或者希望在回答这个问题时得到解决。
crockeea 2014年

11
来自ghc.haskell.org/trac/ghc/wiki/ReportABug:“如有疑问,请报告您的错误。” 您不应该感到难过,尤其是因为这里有足够多的真正有经验的散客不知道如何回答您的问题。对于GHC开发人员而言,这样的测试用例可能确实有价值。无论如何,祝你好运!更新了如果您要提交票证的问题
jberryman 2014年

Answers:


4

简短的答案:

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

  • “自动专业化是可传递的吗?”
  • 我是否应该只期望(+)具有明确的语用上的过渡性专业知识?
  • (显然是故意的)这是GHC的错误吗?它与文档不一致吗?

AFAIK,答案是“否”,多数是,但还有其他方法,“不是”。

代码内联和类型应用程序专业化是速度(执行时间)和代码大小之间的权衡。默认级别可以加快速度,而不会膨胀代码。选择更详尽的级别由程序员通过SPECIALISE编译指示来决定 。

说明:

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

假设f是一个函数,其类型包括a受类型类约束的类型变量C a。默认情况下f,GHC专门针对类型应用程序(替换at),如果f在源代码中使用该类型应用程序调用了该类型应用程序(a)同一模块中的任何函数,或(b)如果f被标记INLINABLE,则导入的 任何其他模块 f来自B。因此,自动专业化不是可传递的,它仅涉及的源代码INLINABLE导入和调用的函数。A

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

instance (Num r, Unbox r) => Num (Qux r) where
    (+) = quxAdd

quxAdd (Qux x) (Qux y) = Qux $ U.zipWith (+) x y
  • quxAdd不是由专门导入的MainMain导入的实例字典Num (Qux Int),并且该字典包含quxAdd在的记录中(+)。但是,尽管导入了词典,但词典中使用的内容并未导入。
  • plus不调用quxAdd,它使用存储(+)在实例字典中的记录的函数Num t。该词典Main由编译器在调用站点(中)设置。
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.