[GHC] #13080: Memory leak caused by nested monadic loops

GHC ghc-devs at haskell.org
Wed Jan 11 22:09:28 UTC 2017


#13080: Memory leak caused by nested monadic loops
-------------------------------------+-------------------------------------
        Reporter:  Feuerbach         |                Owner:
            Type:  bug               |               Status:  new
        Priority:  normal            |            Milestone:  8.2.1
       Component:  Compiler          |              Version:  8.0.1
      Resolution:                    |             Keywords:
Operating System:  Unknown/Multiple  |         Architecture:
 Type of failure:  Runtime           |  Unknown/Multiple
  performance bug                    |            Test Case:
      Blocked By:                    |             Blocking:
 Related Tickets:                    |  Differential Rev(s):
       Wiki Page:                    |
-------------------------------------+-------------------------------------

Comment (by nomeata):

 >  I thought that the state hack doesn't change the arity of functions,
 just marks them single-entry.

 That’s how it works, but the ultimate goal of the state hack (the way I
 see it) is to eta-expand them. Usually this works by treating a
 `State#`-typed lambda as one-shot:
 {{{
 foo = bar x >> baz y
 }}}
 will, after inlining `>>` (if that happens, but it usually does for IO)
 look like this:
 {{{
 foo =
   let a = bar x
       b = baz y
   in \s0 -> case a s0 of (_,s1) -> b s1
 }}}
 which is usually bad because of the allocations of `a` and `b`. (But I say
 “usually” because it is good if `bar x` is expensive and `foo` is used
 many times – these are the instances when people complain about the state
 hack removing this sharing).

 Now because `s0` is treated as one-shot, two optimizations are enabled,
 both of which have ultimately the same effect:
 * The inliner feels empowered to inline `a` and `b` into the lambda,
 because it is one shot. Then we obtain
     {{{
     foo = \s0 -> case bar x s0 of (_,s1) -> baz y s1
     }}}
     which we want.
 * The arity analysis uses the one-shot information on the lambda to
 determine that `foo` has arity 1 and eta-expands it:
     {{{
     foo = \s ->
         (let a = bar x
             b = baz y
         in \s0 -> case a s0 of (_,s1) -> b s1) s
     }}}
     which then gets simplified to
     {{{
     foo = \s -> case bar x s of (_,s1) -> baz y s1
     }}}
     again.

 I once had an idea for a less intrusive state hack variant which would do
 this eta-expansion directly (but otherwise do not meddle with the one-
 shotness of `State#`-typed lambdas, which can be wrong), but did not
 follow through with it. See [ticket:9388#comment:6] for some background.

 But this is all `State#`-specific, while your bug is about abstractly
 monad code, where the compiler may now use knowledge about `IO`’s bind,
 right?

--
Ticket URL: <http://ghc.haskell.org/trac/ghc/ticket/13080#comment:13>
GHC <http://www.haskell.org/ghc/>
The Glasgow Haskell Compiler


More information about the ghc-tickets mailing list