来自GHC 7.6 的文档:
[Y]你通常甚至不需要SPECIALIZE pragma.在编译模块M时,GHC的优化器(带-O)自动考虑在M中声明的每个顶级重载函数,并将其专门用于在M中调用它的不同类型.优化器还考虑每个导入的INLINABLE重载函数,并将其专门用于M中调用的不同类型.
和
此外,给定函数f的SPECIALIZE编译指示,GHC将自动为f调用的任何类型类重载函数创建特殊化,如果它们与SPECIALIZE编译指示位于同一模块中,或者它们是INLINABLE; 等等,过渡性的.
因此GHC应该自动专门化一些INLINABLE 没有编译指示标记的/大多数/所有(?)函数,如果我使用显式编译指示,则特化是可传递的.我的问题是:auto -specialization 是否具有传递性?
具体来说,这是一个小例子:
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
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
GHC专门调用plus,但并没有专门(+)的Qux Num杀死性能实例.
但是,一个明确的pragma
{-# SPECIALIZE plus :: Foo (Qux Int) -> Foo (Qux Int) -> Foo (Qux Int) #-}
Run Code Online (Sandbox Code Playgroud)
如文档所示,导致传递专业化,因此(+)是专门的,代码快30倍(两者都编译-O2).这是预期的行为吗?我是否应该只期望(+)具有明确的编译指示的传递?
UPDATE
7.8.2的文档没有改变,行为是相同的,所以这个问题仍然是相关的.
据我了解,这个问题的要点如下:
- “自动专业化是否具有传递性?”
- 我是否应该只期望 (+) 通过显式编译指示传递地专门化?
- (显然是故意的)这是 GHC 的错误吗?与文档不一致吗?
AFAIK,答案是否定的,大部分是肯定的,但还有其他方法,也不是。
代码内联和类型应用程序专门化是速度(执行时间)和代码大小之间的权衡。默认级别可以在不使代码膨胀的情况下获得一些加速。选择更详尽的级别由程序员通过编译指示自行 SPECIALISE决定。
优化器还考虑每个导入的 INLINABLE 重载函数,并将其专门用于 M 中调用它的不同类型。
假设f是一个函数,其类型包括a受类型类约束的类型变量C a。默认情况下,GHC 专门f针对类型应用程序(替换a)t,如果f在 (a) 同一模块中的任何函数的源代码中使用该类型应用程序调用,或者 (b) 如果f标记为,则导入的INLINABLE任何其他模块 从。因此,自动特化不是传递性的,它只涉及在源代码中导入和调用的函数 fBINLINABLEA。
在您的示例中,如果您重写实例,Num如下所示:
instance (Num r, Unbox r) => Num (Qux r) where
(+) = quxAdd
quxAdd (Qux x) (Qux y) = Qux $ U.zipWith (+) x y
Run Code Online (Sandbox Code Playgroud)
quxAdd不是由 专门导入的Main。Main导入 的实例字典Num (Qux Int),并且该字典包含quxAdd在 的记录中(+)。然而,虽然导入了词典,但词典中使用的内容却没有导入。plus不调用,它使用的实例字典中quxAdd存储的记录函数。该字典由编译器在调用站点(in )设置。(+)Num tMain