Add INLINE pragamas on Traversable default methods
authorSimon Peyton Jones <simonpj@microsoft.com>
Wed, 21 Dec 2016 11:38:50 +0000 (11:38 +0000)
committerSimon Peyton Jones <simonpj@microsoft.com>
Wed, 21 Dec 2016 12:26:24 +0000 (12:26 +0000)
commitd250d493d1dbe0bcfb19122ab3444c9450babdca
tree5c9094ce20133bf6afe68078df39b12b7b7374f4
parentc66dd05c8d02e2b7df825ed2f13b79fb3a16ab58
Add INLINE pragamas on Traversable default methods

I discovered, when debugging a performance regression in
the compiler, that the list instance of mapM was not being
inlined at call sites, with terrible runtime costs.

It turned out that this was a serious (but not entirely obvious)
omission of an INLINE pragmas in the class declaration for
Traversable.  This patch fixes it.  I reproduce below the
Note [Inline default methods], which I wrote at some length.

We may well want to apply the same fix in other class declarations
whose default methods are often used.

{- Note [Inline default methods]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Consider

   class ... => Traversable t where
       ...
       mapM :: Monad m => (a -> m b) -> t a -> m (t b)
       mapM = traverse   -- Default method

   instance Traversable [] where
       {-# INLINE traverse #-}
       traverse = ...code for traverse on lists ...

This gives rise to a list-instance of mapM looking like this

  $fTraversable[]_$ctaverse = ...code for traverse on lists...
       {-# INLINE $fTraversable[]_$ctaverse #-}
  $fTraversable[]_$cmapM    = $fTraversable[]_$ctraverse

Now the $ctraverse obediently inlines into the RHS of $cmapM, /but/
that's all!  We get

  $fTraversable[]_$cmapM = ...code for traverse on lists...

with NO INLINE pragma!  This happens even though 'traverse' had an
INLINE pragma becuase the author knew it should be inlined pretty
vigorously.

Indeed, it turned out that the rhs of $cmapM was just too big to
inline, so all uses of mapM on lists used a terribly inefficient
dictionary-passing style, because of its 'Monad m =>' type.  Disaster!

Solution: add an INLINE pragma on the default method:

   class ... => Traversable t where
       ...
       mapM :: Monad m => (a -> m b) -> t a -> m (t b)
       {-# INLINE mapM #-}     -- VERY IMPORTANT!
       mapM = traverse
libraries/base/Data/Traversable.hs