[GHC] #14052: Significant GHCi speed regression with :module and `let` in GHC 8.2.1

GHC ghc-devs at haskell.org
Tue Aug 1 08:35:42 UTC 2017


#14052: Significant GHCi speed regression with :module and `let` in GHC 8.2.1
-------------------------------------+-------------------------------------
        Reporter:  RyanGlScott       |                Owner:  (none)
            Type:  bug               |               Status:  new
        Priority:  high              |            Milestone:  8.4.1
       Component:  GHCi              |              Version:  8.2.1-rc2
      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 simonpj):

 Thanks for characterising this so well.  Here are some thoughts.

 * The `GlobalRdrEnv` maps an `OccName` to a `[GlobalRdrElt]`.  This was
 designed to handle the case where there are a handful of things `A.f`,
 `B.f`, etc. in scope, all with unqualified name `f`.  The list is not
 expected to be long.

 * But in this case, I believe that that the list is getting long; hence
 the problem.  Adding a DEBUG warning for this situation would be good.

 * Why is the list getting long?  Becuase we have
 {{{
   ghci> let x = True    -- Binds Ghci1.x
   ghci> let x = False   -- Binds Ghci2.x
   ghci> let x = True    -- Binds GHci3.x
   ...etc...
 }}}
   All those Ids are (rightly) kept in the `ic_tythings`.  But they are
 ''also'' all kept in the `ic_rn_gbl_env`.  (`Note [The interactive
 package]` in `HscTypes` and the following notes are of some help.)

 * I can't work out why we keep the shadowed `x`'s in the `ic_rn_gbl_env`.
 If we simply deleted them, all would be well.  After all, '''we do not
 expect the user to be able to refer to an old `x` with a qualified name
 `Ghci1.x`'''.

 * But consider this:
 {{{
   ghci> :load M    -- Brings `x` and `M.x` into scope
   ghci> x
   ghci> "Hello"
   ghci> M.x
   ghci> "hello"
   ghci> let x = True  -- Shadows `x`
   ghci> x             -- The locally bound `x`
                       -- NOT an ambiguous reference
   ghci> True
   ghci> M.x           -- M.x is still in scope!
   ghci> "Hello"
 }}}
   So when we add `x = True` we must not delete the `M.x` from the
 `GlobalRdrEnv`; rather we just want to make it "qualified only"; hence the
 `mk_fake-imp_spec` in `shadowName`.

 * Side note: this is similar to the Template Haskell case, described in
 `Note [GlobalRdrEnv shadowing]` in `RdrName`.
 {{{
       module M where
         f x = h [d| f = ....f....M.f... |]
 }}}
   In the declaration quote, the unqualified `f` should refer to the `f`
 bound by the quote, but the qualified `M.f` should refer to the top-level
 `f`.  So we don't want to delete that top-level binding from the
 `GlobalRdrEnv`; we just want to make it "qualified only"; hence the
 `mk_fake-imp_spec` in `shadowName`.

 My conclusion: we want a way to delete from the `GlobalRdrEnv` things
 bound by GHCi (like `Ghci1.x`), which we do not want to refer to in a
 qualified way.  How can we distinguish `Ghci1.x` from `M.x`?  Two
 possibilities

 * Easy but a bit hacky: we can look at the package in the `Name`.

 * In some ways nicer: use the `is_qual` field of the `ImpDeclSpec`.
 Currently it's a `Bool`:
   * True => `M.x` in in scope, but `x` is not
   * False => Both `M.x` and `x` are in scope

   We could provide a third possibility, to say that `x` is in scope but
 `M.x` is not.  We could use that for GHCi-bound Ids.

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


More information about the ghc-tickets mailing list