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

StackOverflow https://stackoverflow.com/questions/21356897

質問

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.

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top