[GHC] #11383: CAFs lose sharing due to implicit call stacks

#11383: CAFs lose sharing due to implicit call stacks
-------------------------------------+-------------------------------------
Reporter: simonmar | Owner: gridaphobe
Type: bug | Status: new
Priority: normal | Milestone: 8.0.1
Component: Compiler | Version: 8.0.1-rc1
Keywords: | Operating System: Unknown/Multiple
Architecture: | Type of failure: None/Unknown
Unknown/Multiple |
Test Case: | Blocked By:
Blocking: | Related Tickets:
Differential Rev(s): | Wiki Page:
-------------------------------------+-------------------------------------
The implicit call stack machinery adds a constraint to CAFs, which loses
sharing in some cases. The regression is fixed by -O (actually `-ffull-
laziness`), but it is surprising nonetheless, and might cause problems for
people using GHCi or other places where -O is turned off.
For example:
{{{
{-# LANGUAGE NoMonomorphismRestriction #-}
module Main where
import System.Environment
fib :: Integer -> Integer
fib n = if n < 2 then 1 else fib (n-1) + fib (n-2)
x = if fib 3 > 20 then error "x" else fib 30
main = do
[n] <- getArgs
case n of
"a" -> print (x + x)
"b" -> print x
}}}
Try it as follows (requires 8.0+):
{{{
$ ghc imp.hs -fforce-recomp -O -fno-full-laziness
$ ./imp a +RTS -t
2692538
<

#11383: CAFs lose sharing due to implicit call stacks -------------------------------------+------------------------------------- Reporter: simonmar | Owner: gridaphobe Type: bug | Status: new Priority: normal | Milestone: 8.0.1 Component: Compiler | Version: 8.0.1-rc1 Resolution: | Keywords: Operating System: Unknown/Multiple | Architecture: | Unknown/Multiple Type of failure: None/Unknown | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- Comment (by simonmar): We may or may not consider this a bug, so I'm reporting it for discussion. Arguably it's the same as using any overloaded function in a CAF together with `NoMonomorphismRestriction`, the surprising bit is that `error` is now "overloaded". -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/11383#comment:1 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#11383: CAFs lose sharing due to implicit call stacks -------------------------------------+------------------------------------- Reporter: simonmar | Owner: gridaphobe Type: bug | Status: new Priority: normal | Milestone: 8.0.1 Component: Compiler | Version: 8.0.1-rc1 Resolution: | Keywords: Operating System: Unknown/Multiple | Architecture: | Unknown/Multiple Type of failure: None/Unknown | Test Case: Blocked By: | Blocking: Related Tickets: #11298 | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- Changes (by gridaphobe): * related: => #11298 Comment: As you said, this is really just the monomorphism restriction (or in this case the lack thereof) at work. The two pieces that contribute to this behavior are: 1. `error` is now overloaded with an implicit parameter, the call-stack. 2. GHC infers implicit parameters (including call-stacks) as needed, unless there's an explicit signature or the monomorphism restriction applies. There's a similar report in #11298, see the two definitions of `fooHelper`. We could avoid this (potentially confusing) behavior for the most part by not inferring call-stacks for top-level definitions. I'm a bit reluctant to do so because it makes call-stacks less like implicit parameters, and would require another special case in the type-checker. One of the things I like about the current version of the call-stack solver is how similar implicit call-stacks are to regular implicit parameters. --- I'm actually more concerned about the fact that `-ffull-laziness` changes the behavior. Does this mean we're caching the first call-stack? That would be completely wrong; it would be wrong for any implicit parameter. -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/11383#comment:2 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#11383: CAFs lose sharing due to implicit call stacks -------------------------------------+------------------------------------- Reporter: simonmar | Owner: gridaphobe Type: bug | Status: new Priority: normal | Milestone: 8.0.1 Component: Compiler | Version: 8.0.1-rc1 Resolution: | Keywords: Operating System: Unknown/Multiple | Architecture: | Unknown/Multiple Type of failure: None/Unknown | Test Case: Blocked By: | Blocking: Related Tickets: #11298 | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- Comment (by simonmar): `-ffull-laziness` lifts the expensive computation outside the implicit parameter, so recovering the sharing that was lost by the introduction of the implicit parameter constraint. It doesn't change the meaning. This is only an issue for CAFs, because functions already have a lambda at the outside so there's no sharing to lose. (CAFs are a notorious problem for stack tracing, incidentally, because you get to choose between losing sharing and losing information) -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/11383#comment:3 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#11383: CAFs lose sharing due to implicit call stacks -------------------------------------+------------------------------------- Reporter: simonmar | Owner: gridaphobe Type: bug | Status: new Priority: normal | Milestone: 8.0.1 Component: Compiler | Version: 8.0.1-rc1 Resolution: | Keywords: Operating System: Unknown/Multiple | Architecture: | Unknown/Multiple Type of failure: None/Unknown | Test Case: Blocked By: | Blocking: Related Tickets: #11298 | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- Comment (by simonmar): I suppose if what you were trying to share was itself something that had an implicit call stack parameter, then full-laziness wouldn't be able to fix it. Which is a bigger problem, but still arguably the same issue that the monomorphism restriction was intended to fix. The user gets to choose between having sharing or a call stack when they write the type signature. -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/11383#comment:4 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#11383: CAFs lose sharing due to implicit call stacks -------------------------------------+------------------------------------- Reporter: simonmar | Owner: gridaphobe Type: bug | Status: new Priority: normal | Milestone: 8.0.1 Component: Compiler | Version: 8.0.1-rc1 Resolution: | Keywords: Operating System: Unknown/Multiple | Architecture: Type of failure: Runtime | Unknown/Multiple performance bug | Test Case: Blocked By: | Blocking: Related Tickets: #11298 | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- Changes (by chak): * cc: chak (added) Comment: Haskell performance is tricky to reason about as it is. Adding more complexity seems worrying. -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/11383#comment:6 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#11383: CAFs lose sharing due to implicit call stacks -------------------------------------+------------------------------------- Reporter: simonmar | Owner: gridaphobe Type: bug | Status: patch Priority: normal | Milestone: 8.0.1 Component: Compiler | Version: 8.0.1-rc1 Resolution: | Keywords: Operating System: Unknown/Multiple | Architecture: Type of failure: Runtime | Unknown/Multiple performance bug | Test Case: Blocked By: | Blocking: Related Tickets: #11298 | Differential Rev(s): Phab:D1912 Wiki Page: | -------------------------------------+------------------------------------- Changes (by gridaphobe): * status: new => patch * differential: => Phab:D1912 Comment: This will be (partially) fixed by D1912, which prevents GHC from inferring CallStack constraints for top-level definitions. You could still create the issue by making `x` a local binder inside `main`, but then again, the monomorphism restriction will usually be turned on and will prevent the inference. On a related note, do we have a warning (or less severely, a note) for when we infer a polymorphic type that the monomorphism restriction would have prevented? That might be useful to pair with NoMonomorphismRestriction. -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/11383#comment:7 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#11383: CAFs lose sharing due to implicit call stacks -------------------------------------+------------------------------------- Reporter: simonmar | Owner: gridaphobe Type: bug | Status: patch Priority: normal | Milestone: 8.0.1 Component: Compiler | Version: 8.0.1-rc1 Resolution: | Keywords: Operating System: Unknown/Multiple | Architecture: Type of failure: Runtime | Unknown/Multiple performance bug | Test Case: Blocked By: | Blocking: Related Tickets: #11298 | Differential Rev(s): Phab:D1912 Wiki Page: | -------------------------------------+------------------------------------- Comment (by rwbarton): Replying to [comment:7 gridaphobe]:
On a related note, do we have a warning (or less severely, a note) for when we infer a polymorphic type that the monomorphism restriction would have prevented? That might be useful to pair with NoMonomorphismRestriction.
I agree this would be nice; I've always wished the monomorphism restriction was instead a warning like this. There is `-fwarn-monomorphism-restriction`, but it does the reverse: when the monomorphism restriction is on, it warns about bindings that it applies to. -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/11383#comment:8 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#11383: CAFs lose sharing due to implicit call stacks -------------------------------------+------------------------------------- Reporter: simonmar | Owner: gridaphobe Type: bug | Status: patch Priority: normal | Milestone: 8.0.1 Component: Compiler | Version: 8.0.1-rc1 Resolution: | Keywords: Operating System: Unknown/Multiple | Architecture: Type of failure: Runtime | Unknown/Multiple performance bug | Test Case: Blocked By: | Blocking: Related Tickets: #11298 | Differential Rev(s): Phab:D1912 Wiki Page: | -------------------------------------+------------------------------------- Changes (by j.waldmann): * cc: j.waldmann (added) -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/11383#comment:9 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#11383: CAFs lose sharing due to implicit call stacks -------------------------------------+------------------------------------- Reporter: simonmar | Owner: gridaphobe Type: bug | Status: closed Priority: normal | Milestone: 8.0.1 Component: Compiler | Version: 8.0.1-rc1 Resolution: fixed | Keywords: Operating System: Unknown/Multiple | Architecture: Type of failure: Runtime | Unknown/Multiple performance bug | Test Case: Blocked By: | Blocking: Related Tickets: #11298 | Differential Rev(s): Phab:D1912 Wiki Page: | -------------------------------------+------------------------------------- Changes (by bgamari): * status: patch => closed * resolution: => fixed Comment: Phab:D1912 has been merged. -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/11383#comment:10 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler
participants (1)
-
GHC