[GHC] #10528: compile time performance regression with OverloadedStrings and Text

GHC ghc-devs at haskell.org
Mon Jul 27 12:01:42 UTC 2015


#10528: compile time performance regression with OverloadedStrings and Text
-------------------------------------+-------------------------------------
        Reporter:  jakewheat         |                   Owner:  bgamari
            Type:  bug               |                  Status:  new
        Priority:  high              |               Milestone:  7.10.3
       Component:  Compiler          |                 Version:  7.10.2-rc2
      Resolution:                    |                Keywords:
Operating System:  Linux             |            Architecture:
 Type of failure:  Compile-time      |  Unknown/Multiple
  performance bug                    |               Test Case:
      Blocked By:                    |                Blocking:
 Related Tickets:                    |  Differential Revisions:
-------------------------------------+-------------------------------------

Comment (by simonpj):

 Aha!  RULE `TEXT literal` is terribly fragile:

  * In `GHC.Base` we have
 {{{
 {-# RULES
 "unpack"       [~1] forall a   . unpackCString# a             = build
 (unpackFoldrCString# a)
 "unpack-list"  [1]  forall a   . unpackFoldrCString# a (:) [] =
 unpackCString# a
 }}}
  * `build` inlines in phase 1

 So, transformation goes like this:
 {{{
 Start with
    fromString (unpackCString# "blah"#)

 ---> (simplify: gentle phase)  fire rule "unpack"

    fromString (build (unpackFoldrCString# "blah"#))

 ---> (simplify: phase2)  inline fromString

    Data.Text.pack (build (unpackFoldrCString# "blah"#))

 ---> (simplify: phase1)  inline pack, build, fire rule "unpack-list"

    unstream (map safe (streamList (unpackCString# "blah"#)))
 }}}
 Now you'd think that rule "TEXT literal" would now fire.  But its LHS too
 has been rewritten
 by RULE "unpack" to
 {{{
 "TEXT literal" [ALWAYS] forall a :: Addr#
   unstream (map
               safe
               (streamList
                  @ Char
                  (build @ Char (\ @ b -> unpackFoldrCString# @ b a))))
   = unpackCString# a
 }}}
 You can see this by doing `ghc --show-iface Data/Text/Show.hi`,
 incidentally.

 Why did that happen? Arguably it's a bug: we should not rewrite the LHS of
 a rule with other rules, any more than we should inline functions on the
 LHS of a rule.

 But it betrays a potential flaw in the rule setup. Consider a source-
 program expression `(unnstream (map safe (streamList (unpackCString#
 "foo"))))`.  Since there is a rule for `unpackCString#`, there is no
 guarantee that rule `TEXT literal` will fire before rule `unpack`.  In
 this case we know that the `unpackCString#` call will eventually be
 rewritten back into `unpackCString#`.  But it would be better to express
 that directly by saying
 {{{
 {-# RULES [2] "TEXT literal" forall a.
     unstream (S.map safe (S.streamList (GHC.unpackCString# a)))
       = unpackCString# a #-}
 }}}
 Now the two rules do not compete, and all will be good.

 So there are several things to say here

  * GHC probably should do no inlining and no rule rewriting on LHS of
 rules.  I'll fix that.

  * GHC already warns when an inlining competes with a rule; but it should
 also warn if a rule competes with a rule.  I'll fix that too.

  * To eliminate the warning, rule "TEXT literal" should be fixed as above.

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


More information about the ghc-tickets mailing list