List function pragma discipline

Dear list, I was working on https://ghc.haskell.org/trac/ghc/ticket/10788 (performance regressions involving minimum), and I found it quite unsatisfactory to twiddle with individual functions, based on individual test cases, this way. Of course I can improve things for a combination of functions and testcase, but if so, shouldn’t I be improving others as well? What about other test cases – will they regress? How should I balance the goals of speed, code size and simplicity? This feels more like a random walk rather than hill-climbing, let alone a disciplined approach to the problem. Can we have a disciplined approach? I.e. a set of guidelines that work well in general, can be documented and communicated to the user, and that we can follow in such cases? Here is a rough draft, more to show what I am aiming for, rather than a finished work: * Functions that can take part in list fusion have RULES to rewrite them in terms of foldr/build, but also RULES to write them back. Phase control ensures that this is done by phase 0, and the following applies to phase 0. * Recursive non-polymorphic non-higher-order functions are not marked as INLINE (and thus not inlineable, beside possibly the wrapper of a worker-wrapper split). * Recursive polymorphic non-higher-order functions are marked as INLINEABLE, and SPECIALIZE pragmas are given for common cases. (Example: minimum, maximum). * Recursive higher-order functions are implemented with a local recursive function, to allow for inlining (foldl, foldr, maximumBy), at least if there is a chance of further optimizations due to strictness analysis. Given such a guideline, I would then know what to do with such a ticket, instead of stabbing in the dark. It might mean that we would have to tell a user who observes suboptimal performance in one case that this is unavoidable (we cannot optimize for everyone) and possibly explain how to work around the issue. Or we might find that the discipline could be improved, but then the improvement should be applied to all functions. Thoughts? Joachim -- Joachim “nomeata” Breitner mail@joachim-breitner.de • http://www.joachim-breitner.de/ Jabber: nomeata@joachim-breitner.de • GPG-Key: 0xF0FBF51F Debian Developer: nomeata@debian.org

| Can we have a disciplined approach? I.e. a set of guidelines that work | well in general, can be documented and communicated to the user, and | that we can follow in such cases? I'm all for such a thing. A wiki page would be good. Every item in your list needs at least one example so we can understand what you are saying. Simon | -----Original Message----- | From: Libraries [mailto:libraries-bounces@haskell.org] On Behalf Of | Joachim Breitner | Sent: 07 September 2015 10:08 | To: Haskell Libraries | Subject: List function pragma discipline | | Dear list, | | I was working on | https://ghc.haskell.org/trac/ghc/ticket/10788 (performance regressions | involving minimum), and I found it quite unsatisfactory to twiddle | with individual functions, based on individual test cases, this way. | Of course I can improve things for a combination of functions and | testcase, but if so, shouldn’t I be improving others as well? What | about other test cases – will they regress? How should I balance the | goals of speed, code size and simplicity? | | This feels more like a random walk rather than hill-climbing, let | alone a disciplined approach to the problem. | | Can we have a disciplined approach? I.e. a set of guidelines that work | well in general, can be documented and communicated to the user, and | that we can follow in such cases? | | Here is a rough draft, more to show what I am aiming for, rather than | a finished work: | | * Functions that can take part in list fusion have RULES to rewrite | them in terms of foldr/build, but also RULES to write them back. | Phase control ensures that this is done by phase 0, and the | following applies to phase 0. | | * Recursive non-polymorphic non-higher-order functions are not | marked as INLINE (and thus not inlineable, beside possibly the | wrapper of a worker-wrapper split). | * Recursive polymorphic non-higher-order functions are marked as | INLINEABLE, and SPECIALIZE pragmas are given for common cases. | (Example: minimum, maximum). | * Recursive higher-order functions are implemented with a local | recursive function, to allow for inlining (foldl, foldr, | maximumBy), at least if there is a chance of further optimizations | due to strictness analysis. | | | Given such a guideline, I would then know what to do with such a | ticket, instead of stabbing in the dark. It might mean that we would | have to tell a user who observes suboptimal performance in one case | that this is unavoidable (we cannot optimize for everyone) and | possibly explain how to work around the issue. Or we might find that | the discipline could be improved, but then the improvement should be | applied to all functions. | | Thoughts? | Joachim | | | -- | Joachim “nomeata” Breitner | mail@joachim-breitner.de • http://www.joachim-breitner.de/ | Jabber: nomeata@joachim-breitner.de • GPG-Key: 0xF0FBF51F | Debian Developer: nomeata@debian.org

On Mon, Sep 7, 2015 at 5:08 AM, Joachim Breitner
Given such a guideline, I would then know what to do with such a ticket, instead of stabbing in the dark. It might mean that we would have to tell a user who observes suboptimal performance in one case that this is unavoidable (we cannot optimize for everyone) and possibly explain how to work around the issue. Or we might find that the discipline could be improved, but then the improvement should be applied to all functions.
I'm all for this. I've messed around with a lot of list-fusion rewrite rules, and though I have a good handle on them now, it took a good deal of trial and error to learn the art of it. In addition to offering a route to learning, another key aspect of these guidelines should be to keep track of which practices are still relevant. Over the years there've been a number of changes in the details of how rules interact with other optimizations, so it'd be good to air out what practices are still relevant (and why) vs which ones arose from legacy issues (and maybe note why they used to but no longer are a concern) -- Live well, ~wren

I think this is an admirable goal, but I do have a few concerns.
1. The inliner is a finicky beast. A guideline that makes sense for one
function may make less sense for another depending on how the inliner tends
to look at them prior to fusion.
2. There may (I'm not sure) be some combinations that are common enough in
real code that it's worth thinking about them specially.
3. unfoldr is a bit of an oddball, because there are good reasons to want
to inline it every time regardless of fusion.
On Sep 11, 2015 9:24 PM, "wren romano"
On Mon, Sep 7, 2015 at 5:08 AM, Joachim Breitner
wrote: Given such a guideline, I would then know what to do with such a ticket, instead of stabbing in the dark. It might mean that we would have to tell a user who observes suboptimal performance in one case that this is unavoidable (we cannot optimize for everyone) and possibly explain how to work around the issue. Or we might find that the discipline could be improved, but then the improvement should be applied to all functions.
I'm all for this. I've messed around with a lot of list-fusion rewrite rules, and though I have a good handle on them now, it took a good deal of trial and error to learn the art of it. In addition to offering a route to learning, another key aspect of these guidelines should be to keep track of which practices are still relevant. Over the years there've been a number of changes in the details of how rules interact with other optimizations, so it'd be good to air out what practices are still relevant (and why) vs which ones arose from legacy issues (and maybe note why they used to but no longer are a concern)
-- Live well, ~wren _______________________________________________ Libraries mailing list Libraries@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries
participants (4)
-
David Feuer
-
Joachim Breitner
-
Simon Peyton Jones
-
wren romano