Haskell内联函数可以作为参数传递吗?

时间:2014-08-29 10:32:37

标签: haskell optimization

我们说我将一个小函数f传递给map。 Haskell可以用f内联map来产生一个小的命令循环吗?如果是这样,Haskell如何跟踪f实际上是什么函数?可以用Arrow组合器完成同样的工作吗?

3 个答案:

答案 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。