[GHC] #11735: Optimize coercionKind

GHC ghc-devs at haskell.org
Fri Jan 26 19:51:13 UTC 2018


#11735: Optimize coercionKind
-------------------------------------+-------------------------------------
        Reporter:  goldfire          |                Owner:  (none)
            Type:  task              |               Status:  new
        Priority:  normal            |            Milestone:
       Component:  Compiler          |              Version:  7.10.3
      Resolution:                    |             Keywords:
Operating System:  Unknown/Multiple  |         Architecture:
 Type of failure:  Compile-time      |  Unknown/Multiple
  performance bug                    |            Test Case:
      Blocked By:                    |             Blocking:
 Related Tickets:                    |  Differential Rev(s):
       Wiki Page:                    |
-------------------------------------+-------------------------------------

Comment (by tdammers):

 Replying to [comment:38 goldfire]:
 > I'm sorry -- I don't understand the first part of comment:37. Getting a
 kind should never require getting a role. That's why there is a version of
 `coercionKind` that's a standalone function. Let's assume you got these
 two swapped. Even then, I'm not sure what you're describing; it seems
 you're describing your 'un-refactored" version keeping roles and kinds
 separate.

 I'm sorry, yes, I was being confused there; `coercionKind` and
 `coercionRole` are mutually recursive in the "un-refactored" version only.
 However, the un-refactoring *does* produce a performance improvement, so
 there must be *something* going on here - I assumed that the original
 `coercionKindRole` would ultimately amount to a similar recursion pattern,
 but it probably doesn't.

 > If they are together (as in HEAD), I don't see the quadratic behavior.
 > And yet, something goes terribly wrong in HEAD, even without this
 quadratic behavior. But what?? Or maybe I'm completely missing something
 here.

 I think I have found it. For clarity, this is the relevant code on HEAD:

 {{{
     go (NthCo d co)
       | Just (tv1, _) <- splitForAllTy_maybe ty1
       = ASSERT( d == 0 )
         let (tv2, _) = splitForAllTy ty2 in
         (tyVarKind <$> Pair tv1 tv2, Nominal)

       | otherwise
       = let (tc1,  args1) = splitTyConApp ty1
             (_tc2, args2) = splitTyConApp ty2
         in
         ASSERT2( tc1 == _tc2, ppr d $$ ppr tc1 $$ ppr _tc2 )
         ((`getNth` d) <$> Pair args1 args2, nthRole r tc1 d)

       where
         (Pair ty1 ty2, r) = go co
 }}}

 So the rough shape of the recursion is simple - we hit the `otherwise`
 case repeatedly until we get to the `d == 0` case; O(n). But inside the
 `otherwise` branch, there's this pesky `getNth` call, which is linear in
 `d` (being essentially a linked-list lookup), and another one via
 `nthRole`.

 The problem goes away when we calculate the role at construction time,
 because we are either constructing an NthCo that doesn't wrap another
 NthCo, which makes the role calculation constant-time, or we are
 constructing one that *does* wrap another NthCo, but that one already has
 its role calculated, so it is also constant.

 Hope I'm making sense now.

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


More information about the ghc-tickets mailing list