[GHC] #10678: integer-gmp's runS seems unnecessarily expensive

GHC ghc-devs at haskell.org
Fri Jul 24 09:16:58 UTC 2015


#10678: integer-gmp's runS seems unnecessarily expensive
-------------------------------------+-------------------------------------
        Reporter:  rwbarton          |                   Owner:
            Type:  bug               |                  Status:  new
        Priority:  normal            |               Milestone:
       Component:  Compiler          |                 Version:  7.10.1
  (CodeGen)                          |
      Resolution:                    |                Keywords:
Operating System:  Unknown/Multiple  |            Architecture:
 Type of failure:  Runtime           |  Unknown/Multiple
  performance bug                    |               Test Case:
      Blocked By:                    |                Blocking:
 Related Tickets:                    |  Differential Revisions:
-------------------------------------+-------------------------------------

Comment (by simonpj):

 Reid, indeed it is known: #5916.  (If you find any other tickets about
 this, please add them.)

 I think the best approach would be as in comment:16 of
 ticket:5916#comment:16
  * '''Inline `runS` very late, in `CorePrep`'''

 Some thoughts about this.
  * We'd want a single, magical function which has this behaviour.  Things
 like `unsafeDupablePerformIO` should call `runS`.

  * Now I think about it, I wonder if the magical function should instead
 be this:
 {{{
 runRW# :: (State# s -> (# State s, a)) -> (# State s, a #)
 {-# NOINLINE runRW# #-}
 runRW# f = f realWorld#
 }}}
  That is, all `runRW#` does is to apply its argument to a fresh state
 token.

  Now we can define your `runS` thus:
 {{{
   runS f = case ruNRW# f of (# _, r #) -> r
 }}}
  I think we can inline `runS` freely, which is good because it means that
 more code is exposed to the optimiser (in particular that `case`).

  * Moreover, if we do this, I think we now don't need `lazy`.  Because
 `runRW#`'s strictness signature won't be strict in the `r` part; see `Note
 [unsafeDupablePerformIO has a lazy RHS]` in `GHC.IO`.

  * If `CorePrep` saw `runRW# (f a b)` it can generate `f a b realWorld#`.
 But if it sees `runRW# (\s.e)`, it should generate `e[realWorld#/s]`.
 That is, it should do the beta-reduction on the fly.  That's slightly
 annoying because `CorePrep` doesn't currently carry around a substitution.
 But I suppose that you could literally call `substExpr`; this doesn't
 happen much.

  * Alternatively maybe this could be done in the code generator,
 effectively treating `runRW` as a primop.  That would be a good plan,
 except that by the time we get to STG the program is in ANF, so instead of
 `runRW# (f a b)` we'd have
 {{{
 let g = f a b in ruNRW# g
 }}}
   and before we see the `runRW#` we'll have generated code to allocate a
 closure for `g`.  So we'd need to allow STG syntax (for specified primops)
 to have expressions, not just atoms, as the arguments to the primop.

  This might actually be a Jolly Good Thing.  At the moment `catch# (\s ->
 e1) (\x s -> e2)` allocates two closures before it gets to the `catch#`,
 which is pretty stupid since the first thing we do after pushing the
 exception-catch frame is to execute `e1`.

 I think changes like this would make modest but significant improvements
 to many programs.  If you'd like to dig into it, I'd gladly help.

 Simon

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


More information about the ghc-tickets mailing list