gpt4 book ai didi

haskell - Haskell中全局多态(类)值的重用/内存

转载 作者:行者123 更新时间:2023-12-04 08:06:53 27 4
gpt4 key购买 nike

我关心是否以及何时共享/存储多态“全局”类值,特别是跨模块边界。我已阅读 thisthis ,但它们似乎并不能完全反射(reflect)我的情况,而且我看到的行为与人们对答案的期望不同。

考虑一个公开了一个计算起来可能很昂贵的值的类:

{-# LANGUAGE FlexibleInstances, UndecidableInstances #-}

module A

import Debug.Trace

class Costly a where
costly :: a

instance Num i => Costly i where
-- an expensive (but non-recursive) computation
costly = trace "costly!" $ (repeat 1) !! 10000000

foo :: Int
foo = costly + 1

costlyInt :: Int
costlyInt = costly

和一个单独的模块:
module B
import A

bar :: Int
bar = costly + 2

main = do
print foo
print bar
print costlyInt
print costlyInt

运行 maincostly 产生两个单独的评估(如迹线所示):一个用于 foo , 一个用于 bar .我知道 costlyInt只返回(评估的) costly来自 foo , 因为如果我删除 print foo来自 main然后是第一个 costlyInt变得昂贵。 (我也可以通过将 costlyInt 的类型推广到 foo 来使 Num a => a 无论如何执行单独的评估。)

我想我知道为什么会发生这种行为: Costly 的实例实际上是一个接受 Num 的函数字典并生成 Costly字典。所以编译时 bar并解决对 costly 的引用, ghc 生成一个新的 Costly字典,里面有一个昂贵的 thunk。问题1:我对此是否正确?

有几种方法可以只对 costly 进行一次评估。 , 包含:
  • 将所有内容放在一个模块中。
  • 删除 Num i实例约束,只需定义一个 Costly Int实例。

  • 不幸的是,这些解决方案的类似物在我的程序中是不可行的——我有几个模块以多态形式使用类值,并且只有在顶级源文件中才最终使用具体类型。

    还有一些变化不会减少评估的数量,例如:
  • costly 上使用 INLINE、INLINABLE 或 NOINLINE实例中的定义。 (我没想到这会起作用,但是,嘿,值得一试。)
  • 使用 SPECIALIZE instance Costly Int实例定义中的 pragma。

  • 后者让我感到惊讶——我希望它基本上等同于上面确实有效的第二项。也就是说,我认为它会生成一个特殊的 Costly Int字典,所有 foo , bar , 和 costlyInt会分享。我的问题2:我在这里错过了什么?

    我的最后一个问题:是否有任何相对简单且万无一失的方法来获得我想要的,即所有对 costly 的引用跨模块共享的特定具体类型?从我目前看到的情况来看,我怀疑答案是否定的,但我仍然抱有希望。

    最佳答案

    在 GHC 中控制共享很棘手。 GHC 进行了许多可能影响共享的优化(例如内联、 float 等)。

    在这种情况下,要回答为什么 SPECIALIZE pragma 没有达到预期效果的问题,让我们看一下 B 模块的核心,特别是 bar 的核心。功能:

    Rec {
    bar_xs
    bar_xs = : x1_r3lO bar_xs
    end Rec }

    bar1 = $w!! bar_xs 10000000
    -- ^^^ this repeats the computation. bar_xs is just repeat 1

    bar =
    case trace $fCostlyi2 bar1 of _ { I# x_aDm -> I# (+# x_aDm 2) }
    -- ^^^ this is just the "costly!" string

    这并没有如我们所愿。而不是重用 costly , GHC 决定只内联 costly功能。

    所以我们必须防止 GHC 内联代价高昂,否则计算会重复。我们如何做到这一点?您可能会想添加一个 {-# NOINLINE costly #-} pragma 就足够了,但不幸的是,没有内联的特化似乎不能很好地协同工作:
    A.hs:13:3: Warning:
    Ignoring useless SPECIALISE pragma for NOINLINE function: ‘$ccostly’

    但是有一个技巧可以说服 GHC 做我们想做的事:我们可以写 costly通过以下方式:
    instance Num i => Costly i where
    -- an expensive (but non-recursive) computation
    costly = memo where
    memo :: i
    memo = trace "costly!" $ (repeat 1) !! 10000000
    {-# NOINLINE memo #-}
    {-# SPECIALIZE instance Costly Int #-}
    -- (this might require -XScopedTypeVariables)

    这使我们能够特化 costly , 将同时避免我们计算的内联。

    关于haskell - Haskell中全局多态(类)值的重用/内存,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30312891/

    27 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com