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

Pet*_*lák 23 optimization haskell inline ghc

我有一个模块,它收集并导出一些小函数,例如:

fromEither :: (MonadError e m) => Either e a -> m a
fromEither = either throwError return
Run Code Online (Sandbox Code Playgroud)

要么

withError :: MonadError e m => (e -> e) -> m a -> m a
withError = flip catchError (throwError . f)
Run Code Online (Sandbox Code Playgroud)

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

GHC的文件说:

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

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

我不介意GHC决定不介绍我的一些功能,如果它觉得它们太贵了,但总的来说我想让它们内联而不会污染我的源代码并添加INLINE到处.

Joa*_*ner 19

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

您可以通过运行ghc --show-iface生成的.hi-File 来检查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 }) -}
Run Code Online (Sandbox Code Playgroud)


Gab*_*lez 6

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

  • 如果它是纯粹的功能数量,我会发现这令人惊讶......你确认它是数字,而不是其他一些影响吗? (3认同)
  • 抱歉有疑问,但我在`TidyPgm`中找不到任何这样的代码,在那里做了关于添加展开的决定(`addExternal`中的`show_unfolding`).改变必然会产生一些其他影响. (3认同)