haskell - GHC 中自动特化的传递性

标签 haskell ghc

来自the docs对于 GHC 7.6:

[Y]ou often don't even need the SPECIALIZE pragma in the first place. When compiling a module M, GHC's optimiser (with -O) automatically considers each top-level overloaded function declared in M, and specialises it for the different types at which it is called in M. The optimiser also considers each imported INLINABLE overloaded function, and specialises it for the different types at which it is called in M.

Moreover, given a SPECIALIZE pragma for a function f, GHC will automatically create specialisations for any type-class-overloaded functions called by f, if they are in the same module as the SPECIALIZE pragma, or if they are INLINABLE; and so on, transitively.

所以 GHC 应该自动专门化 some/most/all(?)标记为 INLINABLE 的函数没有 pragma,如果我使用显式 pragma,则特化是传递的。我的问题是: auto-特化是传递性的吗?

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

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 的文档没有改变,并且行为是相同的,所以这个问题仍然相关。

最佳答案

简短回答:

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

  • "is the auto-specialization transitive?"
  • Should I only expect (+) to be specialized transitively with an explicit pragma?
  • (apparently intended) Is this a bug of GHC? Is it inconsistent with the documentation?

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

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

说明:

The optimiser also considers each imported INLINABLE overloaded function, and specialises it for the different types at which it is called in M.

假设f是一个函数,其类型包含类型变量 a受类型类约束 C a 。 GHC 默认专门为 f对于类型应用程序(用 a 替换 t )如果 f在 (a) 同一模块中的任何函数或 (b) if 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不是 Main 专门导入的。 Main导入Num (Qux Int)的实例字典,这本字典包含 quxAdd记录为(+) 。然而,虽然导入了词典,但词典中使用的内容却没有导入。
  • plus不打电话quxAdd ,它使用为 (+) 存储的函数实例字典中记录Num t 。该字典由编译器在调用站点(在 Main 中)设置。

关于haskell - GHC 中自动特化的传递性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21502335/

相关文章:

haskell - BangPatterns 可以出现在哪里

haskell - 如何与外部上下文中的类型相关联

haskell - 如何为 Haskell 项目生成 TAGS?

haskell - 在 GHCi 中找不到 Parsec 模块

haskell - Haskell 中的组合

haskell - 从哪里开始 "Couldn' t 将类型 MyId 与 MyId 匹配”

Haskell:为什么模式匹配中不允许使用++?

haskell - 如何在内部类型声明中重用类型变量

Haskell 高阶函数和结合性

haskell - UndecidableInstances 何时安全?关于 GHC 扩展的一些一般性问题