[GHC] #11707: Don't desugar large lists with build

#11707: Don't desugar large lists with build -------------------------------------+------------------------------------- Reporter: bgamari | Owner: Type: bug | Status: new Priority: normal | Milestone: Component: Compiler | Version: 7.10.3 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: -------------------------------------+------------------------------------- When looking at `T9661` I noticed that a massive list was being inlined into `listArray`, where foldr/build was being applied, turning a bunch of massive static data into a massive block of code. It seems likely that keeping this as static data would be preferable here. -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/11707 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#11707: Don't desugar large lists with build -------------------------------------+------------------------------------- Reporter: bgamari | Owner: Type: bug | Status: new Priority: normal | Milestone: Component: Compiler | Version: 7.10.3 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 bgamari): There is currently a GHC flag which controls whether `build` desugaring is used at all, `-fsimple-list-literals`. To roughly characterize the effect of `build` on allocation, I did a nofib against 3f60ce8751c860a2bd0ddbe87429136a9d97449b run with and without `-fsimple- list-literals`. The major allocation changes were, {{{ -------------------------------------------------------------------------------- Program Size Allocs Runtime Elapsed TotalMem -------------------------------------------------------------------------------- ansi +0.0% +10.8% 0.000 0.004 0.0% bspt +0.1% +2.7% 0.004 0.004 0.0% cacheprof +0.1% +3.3% 0.181 0.181 0.0% eliza -0.2% +0.8% 0.000 0.000 0.0% fluid -0.1% +0.7% 0.004 0.004 0.0% fulsom +0.0% +0.7% 0.125 0.125 -21.4% integrate +0.0% +152.6% 0.081 0.081 +7.4% lift +0.0% +4.5% 0.000 0.000 0.0% nucleic2 -0.3% +2.4% 0.024 0.024 0.0% parser -0.4% +0.6% 0.011 0.012 0.0% reptile -0.0% +0.1% 0.004 0.004 0.0% rewrite +0.1% +96.7% 0.007 0.012 0.0% (those tests with no change in allocations omitted) -------------------------------------------------------------------------------- Min -0.4% -0.2% -1.0% -2.0% -21.4% Max +0.1% +152.6% +0.3% +0.6% +7.4% Geometric Mean -0.0% +1.9% -0.2% -0.2% -0.2% }}} There are a few things to note here: * Allocations, if they change at all, increase without `build` fusion, as we would expect * A `fulsom` had its total memory usage dramatically reduced without `build` fusion; this may be worth further investigation. * Runtime didn't appreciably change in any case * Binary sizes don't change On the other hand, if I compile `T9961` with `-fsimple-list-literals`, I see a dramatic reduction in compiler allocations (from 745044392 to 518841472, about 30%). Unfortunately it's not easy to measure the runtime cost of list construction with this test. I'll try to write up a simple `Criterion` benchmark to do this. -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/11707#comment:1 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#11707: Don't desugar large lists with build -------------------------------------+------------------------------------- Reporter: bgamari | Owner: Type: bug | Status: new Priority: normal | Milestone: Component: Compiler | Version: 7.10.3 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 bgamari): It turns out that getting fusion to fire as it does in `T9961` is actually quite tricky. For instance, while `listArray` fuses (producing a large string of `writeArray#`s) in this case, {{{#!hs arr = listArray (0,10) [ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1 ] }}} It does not in this case, {{{#!hs arr = listArray (0,10) [ 1,1,1,1,1,1,1,1,1,1 ] }}} I then realized that only the prefix of negated elements will be unrolled. For instance, {{{#!hs arr = listArray (0,10) [ -1,-1,-1,1,1,1,1,1,1,1 ] }}} will fuse the first three elements into the construction loop, which will look something like this, {{{#!hs case newArray# ww3_aZu (arrEleBottom) s1#_aXo of _ { (# ipv_aXz, ipv1_aXA #) -> case ww3_aZu of wild3_aXC { __DEFAULT -> case writeArray# ipv1_aXA 0 arr11 ipv_aXz of s4#_aXH { __DEFAULT -> case -# wild3_aXC 1 of wild_XZ { __DEFAULT -> case writeArray# ipv1_aXA 1 arr11 s4#_aXH of s4#1_XYH { __DEFAULT -> case wild_XZ of wild1_X14 { __DEFAULT -> letrec { go_aYj :: [Integer] -> GHC.Prim.Int# -> GHC.Prim.State# s -> GHC.Prim.State# s go_aYj = \ ds_aYk eta_B2 eta1_B1 -> case ds_aYk of _ { [] -> eta1_B1; : y_aYp ys_aYq -> case writeArray# ipv1_aXA eta_B2 y_aYp eta1_B1 of s4#2_XYg { __DEFAULT -> case tagToEnum# (==# eta_B2 wild1_X14) of _ { False -> go_aYj ys_aYq (+# eta_B2 1) s4#2_XYg; True -> s4#2_XYg } } }; } in {- ... -} }}} where `arr11 = __integer -1`. -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/11707#comment:2 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#11707: Don't desugar large lists with build -------------------------------------+------------------------------------- Reporter: bgamari | Owner: Type: bug | Status: new Priority: normal | Milestone: Component: Compiler | Version: 7.10.3 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): Why do `integrate` and `rewrite` regress so much? -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/11707#comment:3 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#11707: Don't desugar large lists with build -------------------------------------+------------------------------------- Reporter: bgamari | Owner: Type: bug | Status: new Priority: normal | Milestone: Component: Compiler | Version: 7.10.3 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 bgamari): Replying to [comment:3 simonpj]:
Why do `integrate` and `rewrite` regress so much?
In the case of `integrate` we have, {{{#!hs let d = (u-l)/8.0 in d * sum [ (f l)*0.5, f (l+d), f (l+(2.0*d)), f (l+(3.0*d)), f (l+(4.0*d)), f (u-(3.0*d)), f (u-(2.0*d)), f (u-d), (f u)*0.5] }}} which is precisely the sort of pattern I was concerned about. -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/11707#comment:4 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#11707: Don't desugar large lists with build -------------------------------------+------------------------------------- Reporter: bgamari | Owner: Type: bug | Status: new Priority: normal | Milestone: Component: Compiler | Version: 7.10.3 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 bgamari): I'm not as certain in the case of `rewrite`, which is a much larger program. This is one possibility, {{{#!hs group_rules = map parse_eqn [ "(a * b) * c = a * (b * c)", "E * x = x", "I(x) * x = E" ] }}} Perhaps the more likely culprit is, {{{#!hs p_term = seQ q_func [p_ident, look_for '(', list_of p_expr ',', look_for ')'] ## p_prim }}} where, `seQ` involves `foldr` on its list argument and was clearly designed expecting fusion. There are also a number of singleton lists, although -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/11707#comment:5 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#11707: Don't desugar large lists with build -------------------------------------+------------------------------------- Reporter: bgamari | Owner: Type: bug | Status: new Priority: normal | Milestone: Component: Compiler | Version: 7.10.3 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 bgamari): I've split out the strange inconsistency in unrolling discussed in comment:2 into another ticket, #11710. -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/11707#comment:6 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#11707: Don't desugar large lists with build -------------------------------------+------------------------------------- Reporter: bgamari | Owner: Type: bug | Status: new Priority: normal | Milestone: Component: Compiler | Version: 7.10.3 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 bgamari): I've pushed a simple approach to avoid `build`-based list desugaring for large lists to Phab:D2007. Here we simply threshold on the list length. I have a few concerns about this approach, * This threshold introduces a "performance cliff", where users will suddenly see drastically different performance characteristics as their literal lists grow. * There isn't really a good way to choose this size. Ideally we'd want a smaller threshold with larger consumers and vice-versa, but we have no way of knowing what will be consuming our list in the desugaring. From a runtime performance perspective, applying `build` more liberally should rarely hurt (it can only expose further optimization opportunities; if no fusion is possible it will eventually get rule-rewritten back to a list) Consequently, the threshold we introduce here is a bit concerning since we are limiting contexts in which we'll use `build`, potentially hiding optimizations. Regardless, `nofib` shows no change in allocations after this change, so it at very least doesn't hurt any of the cases there. Moreover, it shows a 30% reduction in compiler allocations in `T9961` (from 745044392 to 745044392 bytes). -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/11707#comment:7 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#11707: Don't desugar large lists with build -------------------------------------+------------------------------------- Reporter: bgamari | Owner: Type: bug | Status: patch Priority: normal | Milestone: Component: Compiler | Version: 7.10.3 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): Phab:D2007 Wiki Page: | -------------------------------------+------------------------------------- Changes (by bgamari): * status: new => patch * differential: => Phab:D2007 -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/11707#comment:8 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#11707: Don't desugar large lists with build -------------------------------------+------------------------------------- Reporter: bgamari | Owner: Type: bug | Status: patch Priority: normal | Milestone: Component: Compiler | Version: 7.10.3 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): Phab:D2007 Wiki Page: | -------------------------------------+------------------------------------- Comment (by simonpj): See #11710; I think we should abandon the "static tail" idea described in `Note [Desugaring explicit lists]` entirely. Then, to avoid code bloat on very long lists, let's have the patch in this ticket, which ensures that if the list is long we revert to cons-list for. The goal here is just to eliminate mega-code-bloat in extreme cases. (Hence no real need for a flag to control `maxBuildLength`. I don't really like it; but I don't want BOTH hacks. One is enough! Not worth burning much midnight oil on this one. -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/11707#comment:9 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#11707: Don't desugar large lists with build
-------------------------------------+-------------------------------------
Reporter: bgamari | Owner:
Type: bug | Status: patch
Priority: normal | Milestone:
Component: Compiler | Version: 7.10.3
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): Phab:D2007
Wiki Page: |
-------------------------------------+-------------------------------------
Comment (by Ben Gamari

#11707: Don't desugar large lists with build -------------------------------------+------------------------------------- Reporter: bgamari | Owner: Type: bug | Status: merge Priority: normal | Milestone: 8.0.1 Component: Compiler | Version: 7.10.3 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): Phab:D2007 Wiki Page: | -------------------------------------+------------------------------------- Changes (by bgamari): * status: patch => merge * milestone: => 8.0.1 -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/11707#comment:11 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#11707: Don't desugar large lists with build -------------------------------------+------------------------------------- Reporter: bgamari | Owner: Type: bug | Status: merge Priority: normal | Milestone: 8.0.1 Component: Compiler | Version: 7.10.3 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): Phab:D2007 Wiki Page: | -------------------------------------+------------------------------------- Comment (by simonpj): What of comment:9? I'd prefer not to remove complexity as we add it! -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/11707#comment:12 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#11707: Don't desugar large lists with build -------------------------------------+------------------------------------- Reporter: bgamari | Owner: Type: bug | Status: merge Priority: normal | Milestone: 8.0.1 Component: Compiler | Version: 7.10.3 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): Phab:D2007 Wiki Page: | -------------------------------------+------------------------------------- Comment (by simonpj): Ah: see Phab:D2023 -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/11707#comment:13 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#11707: Don't desugar large lists with build -------------------------------------+------------------------------------- Reporter: bgamari | Owner: Type: bug | Status: new Priority: normal | Milestone: 8.0.1 Component: Compiler | Version: 7.10.3 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): Phab:D2007, Wiki Page: | Phab:D2023 -------------------------------------+------------------------------------- Changes (by bgamari): * status: merge => new * differential: Phab:D2007 => Phab:D2007, Phab:D2023 -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/11707#comment:14 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#11707: Don't desugar large lists with build -------------------------------------+------------------------------------- Reporter: bgamari | Owner: Type: bug | Status: patch Priority: normal | Milestone: 8.0.1 Component: Compiler | Version: 7.10.3 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): Phab:D2007, Wiki Page: | Phab:D2023 -------------------------------------+------------------------------------- Changes (by bgamari): * status: new => patch -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/11707#comment:15 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#11707: Don't desugar large lists with build -------------------------------------+------------------------------------- Reporter: bgamari | Owner: Type: bug | Status: closed Priority: normal | Milestone: 8.0.1 Component: Compiler | Version: 7.10.3 Resolution: fixed | Keywords: Operating System: Unknown/Multiple | Architecture: Type of failure: Runtime | Unknown/Multiple performance bug | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Phab:D2007, Wiki Page: | Phab:D2023 -------------------------------------+------------------------------------- Changes (by bgamari): * status: patch => closed * resolution: => fixed Comment: Merged to `ghc-8.0` as 19ab5256a1a0a4e7d4ed0d36973fb43eeeda447f. -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/11707#comment:16 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler
participants (1)
-
GHC