Float-out floats out constructors and lambdas, and maybe it shouldn't
I discovered that in FloatOut I was floating constructors and lambdas out of lambdas; e.g
f x ys = let g [] = (x,x)
g (y:ys) = g ys
in g ys
Here we float the (x,x)
out of the g-loop. That might be good; but it might well not, and it came in the "saves work" part of GHC.Core.Opt.SetLevels.lvlMFE
-- We can save work if we can move a redex outside a value lambda
-- But if float_is_new_lam is True, then the redex is wrapped in a
-- a new lambda, so no work is saved
saves_work = escapes_value_lam && not float_is_new_lam
Nothing there says the thing is a redex, although the comment does say so. There is a separate test for saves_alloc
.
I tripped over this when debugging a perf regression on perf/should_run/T21839r
. It's quite complicated, but I think it boils down to this:
Suppose we have
f a b c d = …(\x y….a..x…y..) …
foo r s = ...(\p q. ... (f True e1 e2 e3)...) ...
If we inline f
we can then float (\x y ..True..x…y..)
outside the \pq.
, giving
foo r s = ...let { lvl = \xy. ...True..x..y..} in (\p q. ... (..lvl...)...) ...
But that can increase allocation by allocating lvl
in every call to foo
.
Maybe better to only float non-HNF things. But what about:
commit e706b70b9c863300a118797992b8aa46ad3e2865
Author: Simon Peyton Jones <simonpj@microsoft.com>
Date: Wed May 9 11:18:12 2007 +0000
Improve full laziness by floating allocations out of value lambdas
This patch simplifies the code slightly, and simultaneously improves
full laziness by floating allocations (lambdas, constructor apps) out
of loops.
See Note [Escaping a value lambda] in SetLevels, which explains.
Whoa! 17 years ago! This ticket is to try only floating redexes.
Using exprIsCheap
makes 'mate' get 10% worse