Optimization 是否有可能使GHC优化(去叶)泛型函数,如反同构?

Optimization 是否有可能使GHC优化(去叶)泛型函数,如反同构?,optimization,haskell,ghc,fusion,catamorphism,Optimization,Haskell,Ghc,Fusion,Catamorphism,我非常喜欢以一种通用的方式处理反变形/反变形的想法,但在我看来,它有一个显著的性能缺陷: 假设我们希望以分类的方式处理树结构-使用泛型描述不同的折叠: 现在我们可以编写如下函数: depth1 :: Tree -> Int depth1 = catam g where g Leaf = 0 g (Tree l r) = max l r 不幸的是,这种方法有一个明显的缺点:在计算过程中,TreeT Int的新实例在fmap中的每一级都被创建,只是被g立即使用

我非常喜欢以一种通用的方式处理反变形/反变形的想法,但在我看来,它有一个显著的性能缺陷:

假设我们希望以分类的方式处理树结构-使用泛型描述不同的折叠:

现在我们可以编写如下函数:

depth1 :: Tree -> Int
depth1 = catam g
  where
    g Leaf       = 0
    g (Tree l r) = max l r
不幸的是,这种方法有一个明显的缺点:在计算过程中,
TreeT Int
的新实例在
fmap
中的每一级都被创建,只是被
g
立即使用。与经典定义相比

depth2 :: Tree -> Int
depth2 (Fix Leaf) = 0
depth2 (Fix (Tree l r)) = max (depth1 l) (depth1 r)
我们的
depth1
将始终较慢,对GC造成不必要的压力。一种解决方案是使用并结合创建和折叠树。但通常我们不想这样做,我们可能希望在一个地方创建一棵树,然后传递到其他地方,以便稍后折叠。或者,多次使用不同的变形


有没有办法使GHC优化深度1?比如内联
catam g
然后内联
g。fmap…
内部?

我相信我找到了答案。我记得读过这本书,这给了我一个解决办法

以前的
catam
定义的问题是它是递归的,因此任何尝试都会被忽略。使用
-ddump siml-ddump to file
编译原始版本,并读取:

但是如果我们将catam定义为

{-# INLINE catam #-}
catam :: (Functor f) => (f a -> a) -> (Fix f -> a)
catam f = let u = f . fmap u . unfix
          in u
然后它不再是递归的,只有
u
内部是递归的。这样,GHC在
depth1
的定义中内联
catam
,并将
fmap
depth1
g
融合在一起-这正是我们想要的:

Main.depth1 =
  \ (w_s1RJ :: Main.Tree) ->
    case Main.$wdepth1 w_s1RJ of ww_s1RM { __DEFAULT ->
    GHC.Types.I# ww_s1RM
    }

Rec {
Main.$wdepth1 [Occ=LoopBreaker] :: Main.Tree -> GHC.Prim.Int#
[GblId, Arity=1, Caf=NoCafRefs, Str=DmdType S]
Main.$wdepth1 =
  \ (w_s1RJ :: Main.Tree) ->
    case w_s1RJ
         `cast` (Main.NTCo:Fix <Main.TreeT>
                 :: Main.Fix Main.TreeT ~# Main.TreeT (Main.Fix Main.TreeT))
    of _ {
      Main.Leaf -> 0;
      Main.Tree l_aar r_aas ->
        case Main.$wdepth1 l_aar of ww_s1RM { __DEFAULT ->
        case Main.$wdepth1 r_aas of ww1_X1So { __DEFAULT ->
        case GHC.Prim.<=# ww_s1RM ww1_X1So of _ {
          GHC.Types.False -> ww_s1RM;
          GHC.Types.True -> ww1_X1So
        }
        }
        }
    }
end Rec }
Main.depth1=
\(w_s1RJ::Main.Tree)->
案例主体.$wdepth1 w_s1RJ的ww_s1RM{uuu DEFAULT->
GHC.Types.I#ww#s1RM
}
记录{
Main.$wdepth1[Occ=LoopBreaker]::Main.Tree->GHC.Prim.Int#
[GblId,Arity=1,Caf=NoCafRefs,Str=DmdType S]
Main.$wdepth1=
\(w_s1RJ::Main.Tree)->
案例w_s1RJ
`cast`(Main.NTCo:Fix
::Main.Fix Main.TreeT~#Main.TreeT(Main.Fix Main.TreeT))
当然{
Main.Leaf->0;
Main.Tree l_aar r_aas->
案例主体.$wdepth1 l_aar的ww_s1RM{uuu默认->
案例主体.$wdepth1 r_aas的ww1_X1So{{uuu_默认值->
案例GHC.Prim.ww_s1RM;
GHC.Types.True->ww1\u X1So
}
}
}
}
结束记录}

这与
depth2

的转储相同,似乎任何递归函数都可以通过将其主体移动到局部绑定(如上面的
catam
中所述)而转换为非递归函数。这看起来是一个简单的步骤,有助于优化。我想知道为什么GHC不会自动执行此操作。我来晚了,但是
+1
树中的某个地方不应该有
g
(或
depth2
)函数来计算树的深度吗?否则,我看不出
depth1
depth2
如何返回除零以外的任何值。而且,我认为
depth1
实际上应该是
depth2
的定义中的
depth2
Main.depth2 =
  \ (w_s1Rz :: Main.Tree) ->
    case Main.$wdepth2 w_s1Rz of ww_s1RC { __DEFAULT ->
    GHC.Types.I# ww_s1RC
    }

Rec {
Main.$wdepth2 [Occ=LoopBreaker] :: Main.Tree -> GHC.Prim.Int#
[GblId, Arity=1, Caf=NoCafRefs, Str=DmdType S]
Main.$wdepth2 =
  \ (w_s1Rz :: Main.Tree) ->
    case w_s1Rz
         `cast` (Main.NTCo:Fix <Main.TreeT>
                 :: Main.Fix Main.TreeT ~# Main.TreeT (Main.Fix Main.TreeT))
    of _ {
      Main.Leaf -> 0;
      Main.Tree l_aaj r_aak ->
        case Main.$wdepth2 l_aaj of ww_s1RC { __DEFAULT ->
        case Main.$wdepth2 r_aak of ww1_X1Sh { __DEFAULT ->
        case GHC.Prim.<=# ww_s1RC ww1_X1Sh of _ {
          GHC.Types.False -> ww_s1RC;
          GHC.Types.True -> ww1_X1Sh
        }
        }
        }
    }
end Rec }
{-# INLINE catam #-}
catam :: (Functor f) => (f a -> a) -> (Fix f -> a)
catam f = let u = f . fmap u . unfix
          in u
Main.depth1 =
  \ (w_s1RJ :: Main.Tree) ->
    case Main.$wdepth1 w_s1RJ of ww_s1RM { __DEFAULT ->
    GHC.Types.I# ww_s1RM
    }

Rec {
Main.$wdepth1 [Occ=LoopBreaker] :: Main.Tree -> GHC.Prim.Int#
[GblId, Arity=1, Caf=NoCafRefs, Str=DmdType S]
Main.$wdepth1 =
  \ (w_s1RJ :: Main.Tree) ->
    case w_s1RJ
         `cast` (Main.NTCo:Fix <Main.TreeT>
                 :: Main.Fix Main.TreeT ~# Main.TreeT (Main.Fix Main.TreeT))
    of _ {
      Main.Leaf -> 0;
      Main.Tree l_aar r_aas ->
        case Main.$wdepth1 l_aar of ww_s1RM { __DEFAULT ->
        case Main.$wdepth1 r_aas of ww1_X1So { __DEFAULT ->
        case GHC.Prim.<=# ww_s1RM ww1_X1So of _ {
          GHC.Types.False -> ww_s1RM;
          GHC.Types.True -> ww1_X1So
        }
        }
        }
    }
end Rec }