[GHC] #10359: Tuple constraint synonym led to asymptotic performance lossage

GHC ghc-devs at haskell.org
Tue Apr 28 16:20:45 UTC 2015


#10359: Tuple constraint synonym led to asymptotic performance lossage
-------------------------------------+-------------------------------------
        Reporter:  axch              |                   Owner:
            Type:  bug               |                  Status:  new
        Priority:  normal            |               Milestone:
       Component:  Compiler          |                 Version:  7.6.3
      Resolution:                    |                Keywords:
Operating System:  Linux             |            Architecture:  x86_64
 Type of failure:  Runtime           |  (amd64)
  performance bug                    |               Test Case:
      Blocked By:                    |                Blocking:
 Related Tickets:                    |  Differential Revisions:
-------------------------------------+-------------------------------------

Comment (by simonpj):

 First of all, very nice example. Thank you for making it so small and easy
 to work with.

 I can see what's happening. The key part is what happens here:
 {{{
 do_step4 :: (Numerical num) => num -> Box a -> Box a
 do_step4 number Box{ func = f, obj = x}
               = Box{ func = f, obj = f number x }
 }}}
 After elaboration (ie making dictionaries explicit) we get this:
 {{{
 do_step4 dn1 number (Box {func = f, obj = x })
   = Box { func = \dn2 -> f ( case dn2 of (f,r) -> f
                            , case dn2 of (f,r) -> r)
         , obj = f dn1 number x }
 }}}
 That's odd!  We expected this:
 {{{
 do_step4 dn1 number (Box {func = f, obj = x })
   = Box { func = f
         , obj = f dn1 number x }
 }}}
 And indeed, the allocation of all those `\dn2` closures is what is causing
 the problem.
 So we are missing this optimisation:
 {{{
    (case dn2 of (f,r) -> f, case dn2 of (f,r) -> r)
 ===>
    dn2
 }}}
 If we did this, then the lambda would look like `\dn2 -> f dn2` which
 could eta-reduce to `f`.
 But there are at least three problems:
  * The tuple transformation above is hard to spot
  * The tuple transformation is not quite semantically right; if `dn2` was
 bottom, the LHS and RHS are different
  * The eta-reduction isn't quite semantically right: if `f` ws bottom, the
 LHS and RHS are different.

 You might argue that the latter two can be ignored because dictionary
 arguments are special;
 indeed we often toy with making them strict.

 But perhaps a better way to avoid the tuple-transformation issue would be
 not to construct that strange expression in the first place. Where is it
 coming from?  It comes from the call to `f` (admittedly applied to no
 arguments) in `Box { ..., func = f }`.  GHC needs a dictionary for
 `(Numerical dum)` (I changed the name of the type variable in `func`'s
 type in the definition of `Box`).  Since it's just a pair GHC says "fine,
 I'll build a pair, out of `Fractional dum` and `Real dum`.  How does it
 get those dictionaries?  By selecting the components of the `Franctional
 dum` passed to `f`.

 If GHC said instead "I need `Numerical dum` and behold I have one in hand,
 it'd be much better. It doesn't because tuple constraints are treated
 specially.  But if we adopted the idea in #10362, we would (automatically)
 get to re-use the `Numerical dum` constraint.  That would leave us with
 eta reduction, which is easier.

 As to what will get you rolling, a good solution is `test3`, which saves
 instantiating and re-generalising `f`. The key thing is to update all the
 fields ''except'' the polymorphic `func` field. I'm surprised you say that
 it doesn't work.  Can you give a (presumably more complicated) example to
 demonstrate? Maybe there's a separate bug!

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


More information about the ghc-tickets mailing list