[GHC] #13227: Loss of full laziness in mapFB
GHC
ghc-devs at haskell.org
Thu Feb 2 12:11:41 UTC 2017
#13227: Loss of full laziness in mapFB
-------------------------------------+-------------------------------------
Reporter: simonpj | Owner:
Type: bug | Status: new
Priority: normal | Milestone:
Component: Compiler | Version: 8.0.1
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:
-------------------------------------+-------------------------------------
I've just discovered this
{{{
g4 x expensive ys = let h = \7 -> y + expensive x
in map h ys
}}}
Of course we'd expect the `(expensive x)` call to be floated out of the
`\y`-abstraction, so that it is only done once.
But it isn't! Here's the simplified core with -O:
{{{
g4 = \ (@ b_aPG)
(@ p_atr)
($dNum_aPP :: Num b)
(x_arW :: p)
(expensive_arX :: p -> b)
(ys_arY :: [b]) ->
map @ b @ b
(\ (y_as0 [OS=ProbOneShot] :: b) ->
+ @ b $dNum_aPP y_as0 (expensive_arX x_arW))
ys_arY
}}}
Yikes! What is happening?
Answer: look at that suspicious `ProbOneShot` on the `\y` above. Read
`Note [Computing one-shot info, and ProbOneShot]` in `Demand.hs`.
When `FloatOut` runs we have
{{{
g4 = \ (@ b_aPL)
(@ p_atw)
($dNum_aPU :: Num b)
(x_as1 :: p)
(expensive_as2 :: p -> b)
(ys_as3 :: [b]) ->
GHC.Base.build @ b
(\ (@ b1_aQs)
(c_aQt [OS=OneShot] :: b -> b1 -> b1)
(n_aQu [OS=OneShot] :: b1) ->
GHC.Base.foldr @ b @ b1
(GHC.Base.mapFB @ b @ b1 @ b
c_aQt
(\ (y_as5 [OS=ProbOneShot] :: b) ->
+ @ b $dNum_aPU y_as5 (expensive_as2 x_as1)))
n_aQu
ys_as3)
}}}
Why is the `\y` marked `ProbOneShot`? Because the occurrence analyser
marked it so, based on the cardinality info from `mapFB`, even though
`mapFB` was not saturated.
So `Demand.argsOneShots` makes a deliberate choice to play risky, and that
choice backfires badly for use of `map`. Not good!
The offending commit, which introduced this behaviour, is (I think)
{{{
commit 80989de947dc7edb55999456d1c1e8c337efc951
Author: Simon Peyton Jones <simonpj at microsoft.com>
Date: Fri Nov 22 17:13:05 2013 +0000
Improve the handling of used-once stuff
Joachim and I are committing this onto a branch so that we can share
it,
but we expect to do a bit more work before merging it onto head.
Nofib staus:
- Most programs, no change
- A few improve
- A couple get worse (cacheprof, tak, rfib)
Investigating the "get worse" set is what's holding up putting this
on head.
The major issue is this. Consider
map (f g) ys
where f's demand signature looks like
f :: <L,C1(C1(U))> -> <L,U> -> .
So 'f' is not saturated. What demand do we place on g?
Answer
C(C1(U))
That is, the inner C1 should stay, even though f is not saturated.
I found that this made a significant difference in the demand
signatures
inferred in GHC.IO, which uses lots of higher-order exception
handlers.
I also had to add used-once demand signatures for some of the
'catch' primops, so that we know their handlers are only called once.
}}}
The commit isn't explicit about exactly what the "significant differences"
are. Moreover note that in the comment the outer C1 is replaced by C, but
nothing like that seems to happen in the code.
This needs investigation.
--
Ticket URL: <http://ghc.haskell.org/trac/ghc/ticket/13227>
GHC <http://www.haskell.org/ghc/>
The Glasgow Haskell Compiler
More information about the ghc-tickets
mailing list