I have a module which collects and exports a bunch of small functions such as:
fromEither :: (MonadError e m) => Either e a -> m a
fromEither = either throwError return
or
withError :: MonadError e m => (e -> e) -> m a -> m a
withError = flip catchError (throwError . f)
It's a good idea to have them inlined, as after specializing to a particular instance of MonadError
it's likely a lot of code will be optimized away.
The GHC's documentation says:
GHC (with
-O
, as always) tries to inline (or “unfold”) functions/values that are “small enough,” thus avoiding the call overhead and possibly exposing other more-wonderful optimisations. Normally, if GHC decides a function is “too expensive” to inline, it will not do so, nor will it export that unfolding for other modules to use.
Does it mean that such functions are most likely to be treated as if they had an INLINE
pragma (i.e. their non-optimised RHS recorded in the interface file)? Or do I have to add INLINE
myself? Does adding it change anything (assuming GHC decides they're "small enough")?
I don't mind if GHC decides not to inline some of my functions, if it feels they're too expensive, but in general I'd like to have them inlined without polluting my source code with adding INLINE
everywhere.
From my experience, such small functions will be inlined automatically and across module borders, if it makes sense.
You can check whether GHC decided to make that possible, by running ghc --show-iface
on the resulting .hi
-File. If it says something about an Unfolding
as in the follwing example, when using this module the function will likely be inlined:
$ 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 }) -}
I want to add to Joachim's answer that the number of functions in the module also influences whether or not ghc
will export them. For example, Pipes.Prelude
from pipes
would show slow-downs on several functions when I started to add several unrelated functions to the same module. Adding INLINABLE
pragmas to these functions restored their original speed.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With