当然,这取决于编译器和传递给编译器的选项。
对于这个特定的示例,如果您在没有优化的情况下进行编译,GHC 会按照您编写的代码生成代码,因此第二个版本包含对id
resp 的调用。到not
. 这比第一个版本效率略低,第一个版本只包含对 的调用not
:
Xors.xor1 :: GHC.Types.Bool -> GHC.Types.Bool -> GHC.Types.Bool
[GblId, Arity=2]
Xors.xor1 =
\ (ds_dkm :: GHC.Types.Bool) (x_aeI :: GHC.Types.Bool) ->
case ds_dkm of _ {
GHC.Types.False -> x_aeI;
GHC.Types.True -> GHC.Classes.not x_aeI
}
Xors.xor2 :: GHC.Types.Bool -> GHC.Types.Bool -> GHC.Types.Bool
[GblId, Arity=1]
Xors.xor2 =
\ (ds_dki :: GHC.Types.Bool) ->
case ds_dki of _ {
GHC.Types.False -> GHC.Base.id @ GHC.Types.Bool;
GHC.Types.True -> GHC.Classes.not
}
(调用仍在生成的程序集中,但核心更具可读性,所以我只发布)。
但是经过优化,两个函数都编译到同一个内核(然后编译到同一个机器代码),
Xors.xor2 =
\ (ds_dkf :: GHC.Types.Bool) (eta_B1 :: GHC.Types.Bool) ->
case ds_dkf of _ {
GHC.Types.False -> eta_B1;
GHC.Types.True ->
case eta_B1 of _ {
GHC.Types.False -> GHC.Types.True;
GHC.Types.True -> GHC.Types.False
}
}
id
GHC eta 扩展了第二个版本并内联了对and的调用not
,你得到了纯粹的模式匹配。
无论有没有优化,第二个等式使用False
或通配符在任何一个版本中都没有区别。
也许编译器优化了这个额外的调用。
如果您要求它进行优化,在像这样的简单情况下,GHC 将消除额外的调用。
让我们想象它是一个非平凡的函数。
这是一个可能的问题。如果代码不够简单,编译器可能无法消除通过定义未提供所有参数的函数而引入的所有调用。但是,GHC 非常擅长这样做并内联调用,因此您需要相当多的非平凡性才能使 GHC 无法消除对它在编译代码时知道的简单函数的调用(它当然永远不会内联对它不知道的函数的调用) '不知道编译有问题的模块时的实现)。
如果是关键代码,请始终检查编译器生成的代码,对于 GHC,相关标志是-ddump-simpl
获取优化后生成的核心,并-ddump-asm
获取生成的程序集。