7

I have a body of code that uses a monad to abstract whether the actual implementation runs inside ST or IO. Removing the extra layer of abstraction and just substituting concrete types gives a huge speedup (~4.5x) due to the inlining and missing typeclass function call overhead. I was thinking of getting some of that performance by using a specialize pragma, but I'm getting a rather meaningless warning from the compiler. I can't make a simple reproduction case as the simple example seems to work, and I don't know what's causing the difference in my actual program.

Basically, my program does this:

{-# LANGUAGE FlexibleInstances, RankNTypes #-}

module STImpl (runAbstractST, MonadAbstractIOST(..), ReaderST) where

import Control.Monad.Reader
import Control.Monad.ST

class Monad m => MonadAbstractIOST m where
    addstuff :: Int -> m Int

type ReaderST s = ReaderT (Int) (ST s)

instance MonadAbstractIOST (ReaderST s) where
    addstuff a = return . (a +) =<< ask

runAbstractST :: (forall s. ReaderST s a) -> a
runAbstractST f = runST $ runReaderT f 99

and

module Main (main) where

import STImpl

import Control.Monad

{-# SPECIALIZE INLINE useAbstractMonad :: ReaderST s Int #-}
useAbstractMonad :: MonadAbstractIOST m => m Int
useAbstractMonad = foldM (\a b -> a `seq` return . (a +) =<< (addstuff b)) 0 [1..50000000]

main :: IO ()
main = do
    let st = runAbstractST useAbstractMonad
    putStrLn . show $ st

Now, here everything seems to work fine. But in my program I get

RULE left-hand side too complicated to desugar
  let {
    $dFunctor :: Functor (RSTSim s)
    [LclId]
    $dFunctor =
      Control.Monad.Trans.Reader.$fFunctorReaderT
        @ (MonadSim.SimState s)
        @ (GHC.ST.ST s)
        (GHC.ST.$fFunctorST @ s) } in
  simulate
    @ (Control.Monad.Trans.Reader.ReaderT
         (MonadSim.SimState s) (GHC.ST.ST s))
    (MonadSim.$fMonadSimReaderT
       @ s
       $dFunctor
       (Control.Monad.Trans.Reader.$fMonadReaderT
          @ (MonadSim.SimState s)
          @ (GHC.ST.ST s)
          (GHC.ST.$fMonadST @ s))
       (Control.Monad.Trans.Reader.$fApplicativeReaderT
          @ (MonadSim.SimState s)
          @ (GHC.ST.ST s)
          $dFunctor
          (Control.Applicative.$fApplicativeST0
             @ s (GHC.ST.$fFunctorST @ s))))

I don't understand what 'left-hand side', 'too complicated' and 'desugar' mean ;-)

It seems I have the same problem as described here: http://marc.info/?l=haskell-cafe&m=133242702914511

How do I diagnose this? How do I figure out what's causing the optimization to be disabled in my program?

Thanks!

NBFGRTW
  • 459
  • 3
  • 11
  • If you're on GHC 7 or newer, have you tried the `INLINEABLE` pragma? This is exactly the situation it exists because of. – Carl Sep 14 '13 at 00:51
  • 1
    I'm on GHC 7.6.3 and I tried it. It's ~2x slower than just INLINE on those functions, and then of course ~10x slower than specializing them by hand + inlining. I wouldn't have expected INLINEABLE to have any advantages over INLINE here, and I don't see why it would help with the specialization. – NBFGRTW Sep 14 '13 at 07:49
  • Side note: the usual way to work with `IO` or `ST` is to use the `MonadPrim` class from `Control.Monad.Primitive`, in the `primitive` package. – dfeuer Jan 08 '15 at 21:04

1 Answers1

1

For what it's worth, on the 7.10 RC1 this error no longer occurs, so it looks like the fix to https://ghc.haskell.org/trac/ghc/ticket/8848 may have helped.

Edward Z. Yang
  • 26,325
  • 16
  • 80
  • 110