Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

When generalizing monad, performance drops nearly 50%

I have code that does some parsing of files according to specified rules. The whole parsing takes place in a monad that is a stack of ReaderT/STTrans/ErrorT.

type RunningRule s a = ReaderT (STRef s LocalVarMap) (STT s (ErrorT String Identity)) a

Because it would be handy to run some IO in the code (e.g. to query external databases), I thought I would generalize the parsing, so that it could run both in Identity or IO base monad, depending on the functionality I would desire. This changed the signature to:

type RunningRule s m a = ReaderT (STRef s LocalVarMap) (STT s (ErrorT String m)) a

After changing the appropriate type signatures (and using some extensions to get around the types) I ran it again in the Identity monad and it was ~50% slower. Although essentially nothing changed, it is much slower. Is this normal behaviour? Is there some simple way how to make this faster? (e.g. combining the ErrorT and ReaderT (and possibly STT) stack into one monad transformer?)

To add a sample of code - it is a thing that based on a parsed input (given in C-like language) constructs a parser. The code looks like this:

compileRule :: forall m. (Monad m, Functor m) =>
        -> [Data -> m (Either String Data)] -- For tying the knot
        -> ParsedRule -- This is the rule we are compiling 
        -> Data -> m (Either String Data) -- The real parsing
compileRule compiled (ParsedRule name parsedlines) = 
    \input -> runRunningRule input $ do
         sequence_ compiledlines
   where
       compiledlines = map compile parsedlines
       compile (Expression expr) = compileEx expr >> return ()
       compile (Assignment var expr) = 
       ...
       compileEx (Function "check" expr) = do
           value <- expr
           case value of
               True -> return ()
               False -> fail "Check failed"
           where
                code = compileEx expr
like image 324
ondra Avatar asked Mar 23 '15 02:03

ondra


1 Answers

This is not so unusual, no. You should try using SPECIALIZE pragmas to specialize to Identity, and maybe IO too. Use -ddump-simpl and watch for warnings about rule left hand sides being too complicated. When specialization doesn't happen as it should, GHC ends up passing around typeclass dictionaries at runtime. This is inherently somewhat inefficient, but more importantly it prevents GHC from inlining class methods to enable further simplification.

like image 153
dfeuer Avatar answered Oct 14 '22 13:10

dfeuer