@@ -1,4 +1,4 @@
1−# Build and Optimization Guide
1+# Build Guide
22
33## Building
44
@@ -103,100 +103,3 @@ Features/modules may get disabled if the kernel/OS does not support it.
103103* File system events notification module requires macOS 10.7+ with
104104 Xcode/macOS SDK installed (depends on `Cocoa` framework). However, we only
105105 test on latest three versions of the OS.
106−
107−## Performance Optimizations
108−
109−A "closed loop" is any streamly code that generates a stream using
110−unfold (or conceptually any stream generation combinator) and ends
111−up eliminating it with a fold (conceptually any stream elimination
112−combinator). It is essentially a loop processing multiple elements in
113−a stream sequence, just like a `for` or `while` loop in imperative
114−programming.
115−
116−Closed loops are generated in a modular fashion by stream generation,
117−transformation and elimination combinators in streamly. Combinators
118−transfer data to the next stream pipeline stage using data constructors.
119−These data constructors are eliminated by the compiler using `stream
120−fusion` optimizations, generating a very efficient loop.
121−
122−However, stream fusion optimization depends on proper inlining of the
123−combinators involved. The fusion-plugin package mentioned earlier
124−fills gaps for several optimizations that GHC does not perform
125−automatically. It automatically inlines the internal definitions
126−that involve the constructors we want to eliminate. In some cases
127−fusion-plugin may not help and programmer may have to annotate the code
128−manually for complete fusion. In this section we mention some of the
129−cases where programmer annotation may help in stream fusion.
130−
131−Remember, you need to worry about performance only where it matters, try
132−to optimize the fast path and not everything blindly.
133−
134−### INLINE annotations
135−
136−It may help to add INLINE annotations on any intermediate functions
137−involved in a closed loop. In some cases you may have to add an inline
138−phase as well as described below.
139−
140−Usually GHC has three inline phases - the first phase is pahse-2, the
141−second phase is phase-1 and the last one is phase-0.
142−
143−#### Early INLINE
144−
145−Generally, you only have to inline the combinators or functions
146−participating in a loop and not the whole loop itself. But sometimes
147−you may want to inline the whole loop itself inside a larger
148−function. In most cases you can just add an INLINE pragma on
149−the function containing the loop. But you may need some special
150−considerations in some (not common) cases.
151−
152−In some cases you may have to use INLINE[2] instead of INLINE which
153−means inline the function early in phase-2. This may sometimes be
154−needed on the because the performance of several combinators in streamly
155−depends on getting inlined in phase-2 and if you use a plain `INLINE`
156−annotation GHC may decide to delay the inlining in some cases. This is
157−not very common but may be needed sometimes. Perhaps GHC can be fixed or
158−we can resolve this using fusion-plugin in future.
159−
160−#### Delayed INLINE
161−
162−When a function is passed to a higher order function e.g. a function
163−passed to `concatMap` or `unfoldMany` then we want the function to be
164−inlined after the higher order is inlined so that proper fusion of the
165−higher order function can occur. For such cases we usually add INLINE[1]
166−on the function being passed to instruct GHC not to inline it too early.
167−
168−### Strictness annotations
169−
170−* Strictness annotations on data, specially the data used as accumulator in
171− folds and scans, can help in improving performance.
172−* Strictness annotations on function arguments can help the compiler unbox
173− constructors in certain cases, improving performance.
174−* Sometimes using `-XStrict` extension can help improve performance, if so you
175− may be missing some strictness annotations. `-XStrict` can be used as an aid
176− to detect missing annotations, using it blindly may regress performance.
177−
178−### Use tail recursion
179−
180−Do not use a strict `foldr` or lazy `foldl` unless you know what you are
181−doing. Use lazy `foldr` for lazily transforming the stream and strict
182−`foldl` for reducing the stream. If you are manually writing recursive
183−code, try to use tail recursion where possible.
184−
185−## Build times and space considerations
186−
187−Haskell, being a pure functional language, confers special powers on
188−GHC. It allows GHC to do whole program optimization. In a closed loop
189−all the components of the loop are inlined and GHC fuses them together,
190−performs many optimizing transformations and churns out an optimized
191−fused loop code. Let's call it whole-loop-optimization.
192−
193−To be able to fuse the loop by whole-loop-optimization all the parts of the
194−loop must be operated on by GHC at the same time to fuse them together. The
195−amount of time and memory required to do so depends on the size of the loop.
196−Huge loops can take a lot of time and memory. We have seen GHC take 4-5 GB of
197−memory when a lot of combinators are used in a single module.
198−
199−If a module takes too much time and space we can break it into multiple
200−modules moving some non-inlined parts in another module. There is
201−another advantage of breaking large modules, it can take advantage of
202−parallel compilation if they do not depend on each other.