我有一个收集和导出一些小功能的模块,例如:
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}到处都是。
答案 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添加到这些函数可以恢复原始速度。