@@ -1,12 +1,12 @@
1# Guidelines for writing well optimized code
1# Guidelines for writing well optimized code
3−Stream operations are always part of a loop. Usually the loop consiste of a
3+Stream operations are always part of a loop. Usually the loop consists of a
4stream generation or an unfold operation followed by stream transformation
4stream generation or an unfold operation followed by stream transformation
5functions (e.g. map) and then a stream elimination operation or a fold
5functions (e.g. map) and then a stream elimination operation or a fold
8The default or most common stream representation used in streamly is `StreamD`
8The default or most common stream representation used in streamly is `StreamD`
9−which is a direct style stream reprensentation. All the direct style
9+which is a direct style stream representation. All the direct style
10operations in a loop "fuse" together to form a tight machine loop eliminating
10operations in a loop "fuse" together to form a tight machine loop eliminating
11any intermediate constructors, therefore, reducing allocations and cpu cost.
11any intermediate constructors, therefore, reducing allocations and cpu cost.
12This elimination of intermediate constructors in a loop is known as stream
12This elimination of intermediate constructors in a loop is known as stream
@@ -74,11 +74,34 @@ The step function of a Fold or Parser:
75* Sometimes you may need an explicit INLINE on the step function.
75* Sometimes you may need an explicit INLINE on the step function.
77+## NOINLINE, isolating the closed loop
79+We want the loop iterations to be optimized and the loop stages to be fused to
80+generate a tighter loop. However, it is not necessarily optimal to inline the
81+whole loop itself into a parent function. For example, consider the following
85+{-# NOINLINE readWriteAfter_Stream #-}
86+readWriteAfter_Stream :: Handle -> Handle -> IO ()
87+readWriteAfter_Stream inh devNull =
88+ let readEx = IP.after_ (hClose inh) (S.unfold FH.read inh)
89+ in S.fold (FH.write devNull) readEx
92+If this is inlined into a parent benchmark group list, this leads to
93+many times performance degradation. That's because inlining the loop
94+into a bigger structure may interfere with the optimization of the loop
95+itself and it may not fuse. Whereas it is desirable to INLINE all
96+the stages of a loop, it is often not useful to inline the whole loop
97+itself, in fact we may have to occasionally use a `NOINLINE` so that the
98+compiler does not inline it.
77## StreamK operations
100## StreamK operations
79StreamK uses foldr/build fusion to a very limited degree. StreamK is not the
102StreamK uses foldr/build fusion to a very limited degree. StreamK is not the
80primary representation in streamly but is used for several operations that
103primary representation in streamly but is used for several operations that
81−cannot scale in StreamD represenation. StreamK is relatively immune to compiler
104+cannot scale in StreamD representation. StreamK is relatively immune to compiler
82optimizations. In some cases you may need an INLINE pragma to improve the
105optimizations. In some cases you may need an INLINE pragma to improve the
83performance.
106performance.
@@ -182,14 +205,14 @@ enough to achieve that. In general, we should try to ensure that higher order
182functions are inlined before or in the same phase as the functions they can
205functions are inlined before or in the same phase as the functions they can
183consume as arguments. This means `StreamD` combinators should not be marked
206consume as arguments. This means `StreamD` combinators should not be marked
184as `INLINE` or `INLINE_EARLY`, instead they should all be marked as
207as `INLINE` or `INLINE_EARLY`, instead they should all be marked as
185−`INLINE_NORMAL` because higher order funcitons like `concatMap`/`map`/`mapM`
208+`INLINE_NORMAL` because higher order functions like `concatMap`/`map`/`mapM`
186etc are marked as `INLINE_NORMAL`. `StreamD` functions in other modules like
209etc are marked as `INLINE_NORMAL`. `StreamD` functions in other modules like
187`Streamly.Data.Array.Storable.Foreign` should also follow the same rules.
210`Streamly.Data.Array.Storable.Foreign` should also follow the same rules.
189## Stream Fusion
212## Stream Fusion
191In StreamD combinators, inlining the inner step or loop functions too early
214In StreamD combinators, inlining the inner step or loop functions too early
192−i.e. in the same pahse or before the outer function is inlined may block stream
215+i.e. in the same phase or before the outer function is inlined may block stream
193fusion opportunities. Therefore, the inner step functions and folding loops are
216fusion opportunities. Therefore, the inner step functions and folding loops are
194marked as INLINE_LATE.
217marked as INLINE_LATE.