当前位置: 首页 > 知识库问答 >
问题:

GHC中自动专门化的传递性

夏青青
2023-03-14

来自GHC 7.6的文档:

[Y]你通常甚至不需要一开始的专业语用。当编译一个模块M时,GHC的优化器(with-o)会自动考虑在M中声明的每个顶级重载函数,并针对在M中调用它的不同类型对其进行专门化。优化器还会考虑每个导入的内联重载函数,并针对在M中调用它的不同类型对其进行专门化。

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,但在quxnum实例中不专门用于调用(+),这会降低性能。

但是,一个显式的杂注

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

共有1个答案

祁彬
2023-03-14

根据我的理解,这个问题的要点如下:

  • “自动专门化是传递的吗?”
  • 我应该只期望(+)用显式的杂注进行传递性专门化吗?
  • (显然是有意的)这是GHC的bug吗?是否与文档不一致?

阿法克,答案是否定的,大部分是肯定的,但还有其他方法,没有。

假设F是一个函数,其类型包括一个类型变量a,该变量受类型类C a约束。默认情况下,GHC将针对一个类型应用程序的F专门化(将a替换为T)如果F在(a)同一模块中的任何函数的源代码中用该类型应用程序调用,或者(b)如果F标记为inlinable,则从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)中设置此字典。
 类似资料:
  • 我有一些代码使用两种不同类型的颜色,每个通道8位和每个通道16位,每个都由一个结构表示。为了有效地重用我的代码,我有一个模板函数可以对它们进行一些渲染。因此,我希望有一个模板函数来获取我的颜色通道的最大值。 我最初的尝试是这样的。我只展示了8 bpc的专业化 这不会在Visual studio 2012中编译。我明白了 1个 对我来说,我觉得这应该行得通,但我一直找不到任何例子。在Speciali

  • 文件有两个驱动程序:(运行时间为~3秒)和(运行时间为~83秒),当使用d专门化用-O3编译时。 其核心是:对于测试,加法代码被专用于s等上的向量,而对于则使用通用向量代码。在第10行,您可以看到GHC编写了的专用版本,而不是第167行的通用版本。专门化的规则在第225行。我相信这条规则应该会在270号线上开火。(调用,因此是应该专门化的地方。) 我的目标是通过专门化使与一样快。我找到了两种方法:

  • 经典背包。 这里有一个转折:在这些物品中,我需要确保我有从不同类别中提取的特定数量(不是最高的,而是确切的数量)。 所以让我们假设我们有类别 null

  • 还尝试在专门化的中进行模板方法专门化: 这一次它编译,但调用原始方法,即 解决方案

  • 考虑代码: 我用不同的编译器(通过编译器资源管理器)测试了代码。 如果Clang 7.0.0编译,而给出错误: :8:20:错误:没有与函数模板专用化“栏”匹配的函数模板 :7:26:注意:已忽略候选模板:无法将'void()'与'int()'匹配 Visual C同意(MSVC 19 2017 RTW): (8) :错误C2912:显式专门化“int Test::bar(void)”不是函数模板

  • 我有一个通用算法,需要访问其模板类型的特征。有一个特征类可以专门用于提供这些特征。 在我的类中使用此算法时,我想将其与类中定义的私有类型一起使用。 然而,专门化只能发生在或全局范围内,而我的类是不可访问的。 是否有可能以某种方式专门化具有私有类型的模板,至少在可访问此类型的范围内? 也许可以将这个专门化声明为一个类?