on ae1ffbf help sign in
streamly/docs/Build.md
209 lines 9.0 kB history raw
1# GHC Haskell
2## Compiler (GHC) Versions
4For best performance use GHC 8.10 or 8.8 along with `fusion-plugin`
5(see below). Benchmarks show that GHC 8.8 has significantly better
6performance than GHC 8.6 in many cases.
8Please do not use GHC 9.0 for sensitive applications, initial
9evaluations have shown severe perf regressions with GHC 9.0.
11GHC versions 8.6 onwards are fully supported along with `fusion-plugin`.
12`fusion-plugin` is not supported for GHC versions below 8.6.
14GHC versions 8.0 onwards are supported without `fusion-plugin`. However, note
15that some packages we depend on (e.g. `network`) may support only last three
16major versions of GHC.
18GHC 8.2.2 may hog memory and hang when building certain applications using
19streamly (particularly the benchmark programs in the streamly package).
20Therefore, we recommend avoiding using the GHC version 8.2.x.
22## Memory requirements
24Building streamly itself may require upto 4GB memory. Depending on the
25size of the application you may require 1-16GB memory to build. For most
26applications up to 8GB of memory should be sufficient.
28To reduce the memory footprint you may want to break big modules into
29smaller ones and reduce unnecessary inlining on large functions. You can
30also use the `-Rghc-timing` GHC option to report the memory usage during
31compilation.
33See the "Build times and space considerations" section below for more
34details.
36## Compilation Options
38### Recommended Options
40Add `fusion-plugin` to the `build-depends` section of your program in
41the cabal file and use the following GHC options:
43```
44 -O2
45 -fdicts-strict
46 -fmax-worker-args=16
47 -fspec-constr-recursive=16
48 -fplugin Fusion.Plugin
49```
51Important Notes:
531. [fusion-plugin](https://hackage.haskell.org/package/fusion-plugin) can
54 improve performance significantly by better stream fusion, many
55 cases. If the perform regresses due to fusion-plugin please open
56 an issue. You may remove the `-fplugin` option for regular builds
57 but it is recommended for deployment builds and performance
58 benchmarking. Note, for GHC 8.4 or lower fusion-plugin cannot be used.
592. In certain cases it is possible that GHC takes too long to compile
60 with `-fspec-constr-recursive=16`, if that happens please reduce the
61 value or remove that option.
623. At the very least `-O -fdicts-strict` compilation options are
63 absolutely required to avoid issues in some cases. For example, the
64 program `main = S.drain $ S.concatMap S.fromList $ S.repeat []` may
65 hog memory without these options.
67See [Explanation](#explanation) for details about these flags.
69### Explanation
71* `-fdicts-strict` is needed to avoid [a GHC
72issue](https://gitlab.haskell.org/ghc/ghc/issues/17745) leading to
73memory leak in some cases.
75* `-fspec-constr-recursive` is needed for better stream fusion by enabling
76the `SpecConstr` optimization in more cases. Large values used with this flag
77may lead to huge compilation times and code bloat, if that happens please avoid
78it or use a lower value (e.g. 3 or 4).
80* `-fmax-worker-args` is needed for better stream fusion by enabling the
81`SpecConstr` optimization in some important cases.
83* `-fplugin=Fusion.Plugin` enables predictable stream fusion
84optimization in certain cases by helping the compiler inline internal
85bindings and therefore enabling case-of-case optimization. In some
86cases, especially in some file IO benchmarks, it can make a difference of
875-10x better performance.
89## Multi-core Parallelism
91Concurrency without a threaded runtime may be a bit more efficient. Do not use
92threaded runtime unless you really need multi-core parallelism. To get
93multi-core parallelism use the following GHC options:
95 `-threaded -with-rtsopts "-N"`
97# Platform Specific
99Streamly supports Linux, macOS and Windows operating systems. Some
100modules and functionality may depend on specific OS kernel features.
101Features/modules may get disabled if the kernel/OS does not support it.
103## Linux
105* File system events notification module is supported only for kernel versions
106 2.6.36 onwards.
108## Mac OSX
110* File system events notification module requires macOS 10.7+ with
111 Xcode/macOS SDK installed (depends on `Cocoa` framework). However, we only
112 test on latest three versions of the OS.
114# Performance Optimizations
116A "closed loop" is any streamly code that generates a stream using
117unfold (or conceptually any stream generation combinator) and ends
118up eliminating it with a fold (conceptually any stream elimination
119combinator). It is essentially a loop processing multiple elements in
120a stream sequence, just like a `for` or `while` loop in imperative
121programming.
123Closed loops are generated in a modular fashion by stream generation,
124transformation and elimination combinators in streamly. Combinators
125transfer data to the next stream pipeline stage using data constructors.
126These data constructors are eliminated by the compiler using `stream
127fusion` optimizations, generating a very efficient loop.
129However, stream fusion optimization depends on proper inlining of the
130combinators involved. The fusion-plugin package mentioned earlier
131fills gaps for several optimizations that GHC does not perform
132automatically. It automatically inlines the internal definitions
133that involve the constructors we want to eliminate. In some cases
134fusion-plugin may not help and programmer may have to annotate the code
135manually for complete fusion. In this section we mention some of the
136cases where programmer annotation may help in stream fusion.
138Remember, you need to worry about performance only where it matters, try
139to optimize the fast path and not everything blindly.
141## INLINE annotations
143It may help to add INLINE annotations on any intermediate functions
144involved in a closed loop. In some cases you may have to add an inline
145phase as well as described below.
147Usually GHC has three inline phases - the first phase is pahse-2, the
148second phase is phase-1 and the last one is phase-0.
150### Early INLINE
152Generally, you only have to inline the combinators or functions
153participating in a loop and not the whole loop itself. But sometimes
154you may want to inline the whole loop itself inside a larger
155function. In most cases you can just add an INLINE pragma on
156the function containing the loop. But you may need some special
157considerations in some (not common) cases.
159In some cases you may have to use INLINE[2] instead of INLINE which
160means inline the function early in phase-2. This may sometimes be
161needed on the because the performance of several combinators in streamly
162depends on getting inlined in phase-2 and if you use a plain `INLINE`
163annotation GHC may decide to delay the inlining in some cases. This is
164not very common but may be needed sometimes. Perhaps GHC can be fixed or
165we can resolve this using fusion-plugin in future.
167### Delayed INLINE
169When a function is passed to a higher order function e.g. a function
170passed to `concatMap` or `unfoldMany` then we want the function to be
171inlined after the higher order is inlined so that proper fusion of the
172higher order function can occur. For such cases we usually add INLINE[1]
173on the function being passed to instruct GHC not to inline it too early.
175## Strictness annotations
177* Strictness annotations on data, specially the data used as accumulator in
178 folds and scans, can help in improving performance.
179* Strictness annotations on function arguments can help the compiler unbox
180 constructors in certain cases, improving performance.
181* Sometimes using `-XStrict` extension can help improve performance, if so you
182 may be missing some strictness annotations. `-XStrict` can be used as an aid
183 to detect missing annotations, using it blindly may regress performance.
185## Use tail recursion
187Do not use a strict `foldr` or lazy `foldl` unless you know what you are
188doing. Use lazy `foldr` for lazily transforming the stream and strict
189`foldl` for reducing the stream. If you are manually writing recursive
190code, try to use tail recursion where possible.
192# Build times and space considerations
194Haskell, being a pure functional language, confers special powers on
195GHC. It allows GHC to do whole program optimization. In a closed loop
196all the components of the loop are inlined and GHC fuses them together,
197performs many optimizing transformations and churns out an optimized
198fused loop code. Let's call it whole-loop-optimization.
200To be able to fuse the loop by whole-loop-optimization all the parts of the
201loop must be operated on by GHC at the same time to fuse them together. The
202amount of time and memory required to do so depends on the size of the loop.
203Huge loops can take a lot of time and memory. We have seen GHC take 4-5 GB of
204memory when a lot of combinators are used in a single module.
206If a module takes too much time and space we can break it into multiple
207modules moving some non-inlined parts in another module. There is
208another advantage of breaking large modules, it can take advantage of
209parallel compilation if they do not depend on each other.