Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Do I need to bother with INLINE/INLINABLE pragmas for small, exported, functions, or will GHC do it for me?

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.

like image 207
Petr Avatar asked Jan 25 '14 22:01

Petr


2 Answers

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 }) -} 
like image 197
Joachim Breitner Avatar answered Sep 20 '22 14:09

Joachim Breitner


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.

like image 39
Gabriella Gonzalez Avatar answered Sep 17 '22 14:09

Gabriella Gonzalez