我们说我将一个小函数f
传递给map
。 Haskell可以用f
内联map
来产生一个小的命令循环吗?如果是这样,Haskell如何跟踪f
实际上是什么函数?可以用Arrow组合器完成同样的工作吗?
答案 0 :(得分:12)
如果f
作为参数传入,那么不,可能不是。如果f
是顶级函数或本地函数的名称,则可能是。
foobar f = ... map f ...
-- Probably not inlined.
foobar = ... map (\ x -> ...) ...
-- Probably inlined.
也就是说,我认为内联和外联之间的大多数性能差异并非来自实际的内联本身,而是来自可能允许的任何其他后续优化。
唯一的方法是确保"关于这些事情是实际编写代码,实际编译它,并看看生成的核心。而知道它是否有所作为(正面或负面)的唯一方法是实际对事物进行基准测试。
答案 1 :(得分:11)
Haskell语言的定义并未要求Haskell实现内联代码或执行任何类型的优化。任何实现都可以自由应用它认为合适的任何优化。
话虽如此,Haskell现在经常使用GHC运行,它确实优化了Haskell代码。对于内联,GHC使用一些启发式方法来决定是否应该内联。一般建议是使用-O2
打开优化并检查编译器的输出。您可以使用-ddump-simpl
查看生成的Core,或使用-ddump-asm
查看汇编代码。其他一些flags也很有用。
如果您看到GHC没有内嵌您想要的内容,您可以使用{-# INLINE foo #-}
和related pragmas向编译器提供提示。
但要小心谨慎地应用优化。通常,程序员会花时间优化程序中对整体性能有可忽略影响的部分。为避免这种情况,强烈建议您首先profile使用您的代码,以便了解您的计划花费大量时间。
答案 2 :(得分:1)
以下是GHC内联作为参数传递的函数的示例:
import qualified Data.Vector.Unboxed as U
import qualified Data.Vector as V
plus :: Int -> Int -> Int
plus = (+)
sumVect :: V.Vector Int -> Int
sumVect = V.foldl1 plus
plus
作为foldl1
的参数传递,这导致对整数向量求和。在Core中,plus
内联并针对未装箱的GHC.Prim.+# :: Int# -> Int# -> Int#
优化:
letrec {
$s$wfoldlM_loop_s759
:: GHC.Prim.Int# -> GHC.Prim.Int# -> GHC.Prim.Int#
$s$wfoldlM_loop_s759 =
\ (sc_s758 :: GHC.Prim.Int#) (sc1_s757 :: GHC.Prim.Int#) ->
case GHC.Prim.tagToEnum# @ Bool (GHC.Prim.>=# sc_s758 ww1_s748)
of _ {
False ->
case GHC.Prim.indexArray#
@ Int ww2_s749 (GHC.Prim.+# ww_s747 sc_s758)
of _ { (# ipv1_X72o #) ->
case ipv1_X72o of _ { GHC.Types.I# y_a5Kg ->
$s$wfoldlM_loop_s759
(GHC.Prim.+# sc_s758 1#) (GHC.Prim.+# sc1_s757 y_a5Kg)
}
};
True -> sc1_s757
}; }
那是GHC.Prim.+# sc1_s757 y_a5Kg
。您可以在函数plus
中添加简单的artihmetic,并查看此Core表达式expand。