[GHC] #13851: Change in specialisation(?) behaviour since 8.0.2 causes 6x slowdown
GHC
ghc-devs at haskell.org
Thu Jun 22 15:37:49 UTC 2017
#13851: Change in specialisation(?) behaviour since 8.0.2 causes 6x slowdown
-------------------------------------+-------------------------------------
Reporter: mpickering | Owner: (none)
Type: bug | Status: new
Priority: high | Milestone: 8.2.1
Component: Compiler | 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):
Here is what is happening
* Before float-out we have
{{{
$stest1mtl = \eta. ...foldr (\x k z. blah) z e...
}}}
Since the first arg of the foldr has no free vars, we float it out to
give
{{{
lvl = \x y z. blah
$stest1mtl = \eta. ...foldr lvl z e...
}}}
* That makes `$stest1mtl` small, so it is inlined at its two call sites
(the first two test case in `main`).
* So now there are two calls to `lvl`, and it is quite big, so it doesn't
get inlined.
* But actually it is much better ''not'' to inline `$stest1mtl`, and
instead (after the foldr/build stuff has happened) to inline `lvl` back
into it.
This kind of thing not new; I trip over it quite often. Generally, given
{{{
f = e
g = ...f..
h = ...g...g..f...
}}}
should we inline `f` into `g`, thereby making `g` big, so it doesn't
inline into `h`? Or should we instead inline `g` into `h`? Sometimes one
is better, sometimes the other; I don't know any systematic way of doing
The Right Thing all the time. It turned out that the early-inline patch
changed the choice, which resulted in the changed performance.
However I did spot several things worth trying out
* In `CoreArity.rhsEtaExpandArity` we carefully do not eta-expand thunks.
But I saw some thunks like
{{{
lvl_s621
= case z_a4NJ of wild_a4OF { GHC.Types.I# x1_a4OH ->
case x_a4NH of wild1_a4OJ { GHC.Types.I# y1_a4OL ->
case GHC.Prim.<=# x1_a4OH y1_a4OL of {
__DEFAULT -> (\ _ (eta_B1 :: Int) -> (wild_a4OF, eta_B1))
1# -> (\ _ (eta_B1 :: Int) -> (wild1_a4OJ, eta_B1))
}}}
Here it really would be good to eta-expand; then that particular `lvl`
could be inlined at its call sites. Here's a change to
`CoreArity.rhsEtaExpandArity` that did the job:
{{{
- | isOneShotInfo os || has_lam e -> 1 + length oss
+ | isOneShotInfo os || not (is_app e) -> 1 + length oss
- has_lam (Tick _ e) = has_lam e
- has_lam (Lam b e) = isId b || has_lam e
- has_lam _ = False
+ is_app (Tick _ e) = is_app e
+ is_app (App f _) = is_app f
+ is_app (Var _) = True
+ is_app _ = False
}}}
Worth trying.
* Now the offending top-level `lvl` function is still not inlined; but it
has a function argument that is applied, so teh call sites look like
{{{
lvl ... (\ab. blah) ...
}}}
When considering inining we do get a discount for the application of the
argument inside `lvl`'s rhs, but it was only a discout of 60, which seems
small considering how great it is to inline a function. Boosting it to
150 with `-funfolding-fun-discount=150` make the function inline, and we
get good code all round. Maybe we should just up the default.
* All the trouble is caused by the early float-out. I think we could try
just elminating it.
--
Ticket URL: <http://ghc.haskell.org/trac/ghc/ticket/13851#comment:5>
GHC <http://www.haskell.org/ghc/>
The Glasgow Haskell Compiler
More information about the ghc-tickets
mailing list