对于小型,导出的函数,我是否需要打扰INLINE / INLINABLE编译指示,或者GHC会为我做这些吗?

时间:2014-01-25 22:09:45

标签: haskell optimization inline ghc

我有一个收集和导出一些小功能的模块,例如:

fromEither :: (MonadError e m) => Either e a -> m a
fromEither = either throwError return

withError :: MonadError e m => (e -> e) -> m a -> m a
withError = flip catchError (throwError . f)

让它们内联是个好主意,因为在专门研究MonadError的特定实例之后,可能会有很多代码被优化掉。

GHC的文件说:

  

GHC(与-O一如既往)尝试内联(或“展开”)“足够小”的函数/值,从而避免调用开销并可能暴露其他更精彩的优化。通常情况下,如果GHC决定某个函数“太昂贵”无法内联,它就不会这样做,也不会导出其他模块的展开使用。

这是否意味着这些函数最有可能被视为具有INLINE编译指示(即它们在接口文件中记录的非优化RHS)?或者我是否必须自己添加INLINE?添加它会改变什么(假设GHC决定它们“足够小”)?

我不介意GHC是否决定不插入我的一些功能,如果觉得它们太贵了,但总的来说我想让它们内联而不会污染我的源代码而添加{{1}到处都是。

2 个答案:

答案 0 :(得分:19)

根据我的经验,如果有意义的话,这些小功能将自动内联并跨模块边界。

您可以通过在生成的ghc --show-iface - 文件上运行.hi来检查GHC是否决定使其成为可能。如果它在下面的示例中说明Unfolding的某些内容,则在使用此模块时,该函数可能会内联:

$ ghc --show-iface /usr/lib/ghc/base-4.6.0.1/Data/Either.hi
Magic: Wanted 33214052,
       got    33214052
...
38da29044ff77a85b08cebca1fed11ad
  either :: forall a c b.
            (a -> c) -> (b -> c) -> Data.Either.Either a b -> c
    {- Arity: 3, HasNoCafRefs, Strictness: LLS,
       Unfolding: (\ @ a
                     @ c
                     @ b
                     f :: a -> c
                     ds :: b -> c
                     ds1 :: Data.Either.Either a b ->
                   case ds1 of wild {
                     Data.Either.Left x -> f x Data.Either.Right y -> ds y }) -}

答案 1 :(得分:6)

我想补充Joachim的答案,模块中的函数数量也会影响ghc是否会导出它们。例如,当我开始向同一模块添加几个不相关的函数时,来自Pipes.Prelude的{​​{1}}会在几个函数上显示减速。将pipes pragma添加到这些函数可以恢复原始速度。