on master help sign in
Add comments explaining how "parallel" works
d0896e1Harendra Kumar
src/Streamly/Streams/Parallel.hs+38−1
@@ -155,9 +155,46 @@ joinStreamVarPar :: (IsStream t, MonadAsync m)
155joinStreamVarPar style ss m1 m2 = mkStream $ \st yld sng stp ->
155joinStreamVarPar style ss m1 m2 = mkStream $ \st yld sng stp ->
156 case streamVar st of
156 case streamVar st of
157 Just sv | svarStyle sv == style && svarStopStyle sv == ss -> do
157 Just sv | svarStyle sv == style && svarStopStyle sv == ss -> do
158+ -- Here, WE ARE IN THE WORKER/PRODUCER THREAD, we know that because
159+ -- the SVar exists. We are running under runOne and the output we
160+ -- produce ultimately will be sent to the SVar by runOne.
161+ --
162+ -- If we came here the worker/runOne is evaluating a `parallel`
163+ -- combinator. In this case, we always fork a new worker for the
164+ -- first component (m1) in the parallel composition and continue to
165+ -- evaluate the second component (m2) in the current worker thread.
166+ --
167+ -- When m1 is serially composed, the worker would evaluate it
168+ -- without any further forks and the resulting output is sent to
169+ -- the SVar and the evaluation terminates. If m1 is a `parallel`
170+ -- composition of two streams the worker would again recurses here.
171+ --
172+ -- Similarly, when m2 is serially composed it gets evaluated here
173+ -- and the resulting output is sent to the SVar by the runOne
174+ -- wrapper. When m2 is composed with `parallel` it will again
175+ -- recurse here and so on until it finally terminates.
176+ --
177+ -- When we create a right associated expression using `parallel`,
178+ -- then m1 would always terminate without further forks or
179+ -- recursion into this routine, therefore, the worker returns
180+ -- immediately after evaluating it. And m2 would continue to
181+ -- fork/recurse, therefore, the current thread always recurses and
182+ -- forks new workers one after the other. This is a tail recursive
183+ -- style execution, m2, the recursive expression always executed at
184+ -- the tail.
185+ --
186+ -- When the expression is left associated, the worker spawned would
187+ -- get the forking/recursing responsibility and then again the
188+ -- worker spawned by that worker would fork, thus creating layer
189+ -- over layer of workers and a chain of threads leading to a very
190+ -- inefficient execution.
158 pushWorkerPar sv (runOne st $ toStream m1)
191 pushWorkerPar sv (runOne st $ toStream m1)
159 foldStreamShared st yld sng stp m2
192 foldStreamShared st yld sng stp m2
193+ _ ->
194+ -- Here WE ARE IN THE CONSUMER THREAD, we create a new SVar, fork
195+ -- worker threads to execute m1 and m2 and this thread starts
196+ -- pulling the stream from the SVar.
160− _ -> foldStreamShared st yld sng stp (forkSVarPar ss m1 m2)
197+ foldStreamShared st yld sng stp (forkSVarPar ss m1 m2)
161
198
162-------------------------------------------------------------------------------
199-------------------------------------------------------------------------------
163-- User facing APIs
200-- User facing APIs