Repeated computations under a lambda

Conal Elliott conal at conal.net
Tue Jul 18 22:35:32 UTC 2017


Thanks very much for this reply, Joachim. I see that
`-fno-do-lambda-eta-expansion` with my example prevents moving the
computation under the lambda where it gets repeatedly evaluated. I don't
understand what this code motion/substitution has to do with eta-expansion.
Is it that the `let` expression itself is eta-expanded? The GHC Users Guide
describes this flag as "eta-expand let-bindings to increase their arity",
which doesn't seem to fit here, since the `let`-bindings are not
function-valued (though the `let` expression is).

Thanks also for the suggestion of using `-dverbose-core2core` to see where
the unwanted substitution happened.

Did you measure whether this really is a problem? The benefits of not
> dealing with dynamically allocated functions might outweigh the cost of
> recalculating sin.


No, I haven't measured. In this case, I'm compiling Haskell to GLSL for
execution on a GPU, where the inner lambda will be over space, which means
at least one application per pixel, so the computations moved under the
inner lambda will be redundantly computed a few millions of times per frame
(and much more with anti-aliasing). Instead, I want to move those
calculations to once per frame and stored in quickly accessed video memory.
As the space-independent computation gets more complex, I expect the saving
to grow.

Thanks again,
-- Conal

On Tue, Jul 18, 2017 at 12:08 PM, Joachim Breitner <mail at joachim-breitner.de
> wrote:

> Hi,
>
>
> Am Dienstag, den 18.07.2017, 08:34 -0700 schrieb Conal Elliott:
> > I'm seeing what looks like repeated computation under a lambda with
> > `-O` and `-O2`. The following definition:
> >
> > > exampleC :: Double -> Double -> Double
> > > exampleC = \ t -> let s = sin t in \ x -> x + s
> >
> > yields this Core:
> >
> > > -- RHS size: {terms: 13, types: 6, coercions: 0}
> > > exampleC :: Double -> Double -> Double
> > > exampleC =
> > >   \ (t_afI6 :: Double) (eta_B1 :: Double) ->
> > >     case eta_B1 of _ { D# x_aj5c ->
> > >     case t_afI6 of _ { D# x1_aj5l ->
> > >     D# (+## x_aj5c (sinDouble# x1_aj5l))
> > >     }
> > >     }
>
> ghc -O -dverbose-core2core shows you that the problem is this phase:
>
> ==================== Simplifier ====================
>   Max iterations = 4
>   SimplMode {Phase = 2 [main],
>              inline,
>              rules,
>              eta-expand,
>              case-of-case}
>
> It does not happen with -fno-do-lambda-eta-expansion (but you’d lose in
> other parts.)
>
> > I'm concerned because many of my uses of such functions involve
> > computations dependent only on `t` (time) but with millions of uses
> > (space) per `t`. (I'm working on a GHC Core plugin (compiling to
> > categories), with one use generating graphics GPU code.)
>
> Did you measure whether this really is a problem? The benefits of not
> dealing with dynamically allocated functions might outweigh the cost of
> recalculating sin.
>
> Greetings,
> Joachim
> --
> Joachim Breitner
>   mail at joachim-breitner.de
>   http://www.joachim-breitner.de/
>
> _______________________________________________
> ghc-devs mailing list
> ghc-devs at haskell.org
> http://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-devs
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.haskell.org/pipermail/ghc-devs/attachments/20170718/bfee6e7e/attachment.html>


More information about the ghc-devs mailing list