on master help sign in
performance improvements after maxrate changes
commitb33e9c274bab94ff5b1ce8f7cf36f10c9f4c11b0 authorHarendra Kumar <harendra.kumar@gmail.com> dateMon 6 Aug 2018, 20:51 (8 years ago) parent610c33a
5 files changed+320−150
src/Streamly/SVar.hs+34−28 src/Streamly/Streams/Ahead.hs+162−102 src/Streamly/Streams/Async.hs+120−12 src/Streamly/Streams/SVar.hs+2−5 test/MaxRate.hs+2−3
src/Streamly/SVar.hs+34−28
@@ -561,6 +561,29 @@ doFork action exHandler =
561561 exHandler
562562 runInIO (return tid)
563563
564+-- XXX Can we make access to remainingYields and yieldRateInfo fields in sv
565+-- faster, along with the fields in sv required by send?
566+-- XXX make it noinline
567+{-# INLINE decrementYieldLimit #-}
568+decrementYieldLimit :: SVar t m a -> IO Bool
569+decrementYieldLimit sv =
570+ case remainingYields sv of
571+ Nothing -> return True
572+ Just ref -> do
573+ r <- atomicModifyIORefCAS ref $ \x ->
574+ (if x >= 1 then x - 1 else 0, x)
575+ return $ r >= 1
576+
577+-- XXX increment is not safe when the yield limit was already 0
578+-- and therefore was not decremented. We need yield limit to be
579+-- int and negative if we want this to work.
580+{-# INLINE incrementYieldLimit #-}
581+incrementYieldLimit :: SVar t m a -> IO ()
582+incrementYieldLimit sv =
583+ case remainingYields sv of
584+ Nothing -> return ()
585+ Just ref -> atomicModifyIORefCAS_ ref (+ 1)
586+
564587-- XXX exception safety of all atomic/MVar operations
565588
566589-- TBD Each worker can have their own queue and the consumer can empty one
@@ -648,27 +671,6 @@ workerRateControl sv yinfo winfo = do
648671 beyondMaxRate <- checkRatePeriodic sv yinfo winfo cnt
649672 return $ not (isBeyondMaxYield cnt winfo || beyondMaxRate)
650673
651−-- XXX Can we make access to remainingYields and yieldRateInfo fields in sv
652−-- faster, along with the fields in sv required by send?
653−-- XXX make it noinline
654−decrementYieldLimit :: SVar t m a -> IO Bool
655−decrementYieldLimit sv =
656− case remainingYields sv of
657− Nothing -> return True
658− Just ref -> do
659− r <- atomicModifyIORefCAS ref $ \x ->
660− (if x >= 1 then x - 1 else 0, x)
661− return $ r >= 1
662−
663−-- XXX increment is not safe when the yield limit was already 0
664−-- and therefore was not decremented. We need yield limit to be
665−-- int and negative if we want this to work.
666−incrementYieldLimit :: SVar t m a -> IO ()
667−incrementYieldLimit sv =
668− case remainingYields sv of
669− Nothing -> return ()
670− Just ref -> atomicModifyIORefCAS_ ref (+ 1)
671−
672674-- XXX we should do rate control here but not latency update in case of ahead
673675-- streams. latency update must be done when we yield directly to outputQueue
674676-- or when we yield to heap.
@@ -682,18 +684,22 @@ sendYield sv winfo msg = do
682684 Just yinfo -> workerRateControl sv yinfo winfo
683685 return $ r && rateLimitOk
684686
687+{-# INLINE workerStopUpdate #-}
688+workerStopUpdate :: WorkerInfo -> YieldRateInfo -> Int -> IO ()
689+workerStopUpdate winfo info n = do
690+ i <- readIORef (workerLatencyPeriod info)
691+ when (i /= 0) $ workerUpdateLatency info winfo
692+ when (n == 1) $ do
693+ t <- getTime Monotonic
694+ writeIORef (workerStopTimeStamp info) t
695+
685696{-# INLINABLE sendStop #-}
686697sendStop :: SVar t m a -> WorkerInfo -> IO ()
687698sendStop sv winfo = do
688699 n <- liftIO $ atomicModifyIORefCAS (workerCount sv) $ \n -> (n - 1, n)
689700 case yieldRateInfo sv of
690701 Nothing -> return ()
691− Just info -> do
692− i <- readIORef (workerLatencyPeriod info)
693− when (i /= 0) $ workerUpdateLatency info winfo
694− when (n == 1) $ do
695− t <- getTime Monotonic
696− writeIORef (workerStopTimeStamp info) t
702+ Just info -> workerStopUpdate winfo info n
697703 myThreadId >>= \tid -> void $ send sv (ChildStop tid Nothing)
698704
699705-------------------------------------------------------------------------------
@@ -1099,7 +1105,7 @@ getWorkerLatency yinfo = do
10991105
11001106 let pendingCount = colCount + count
11011107 pendingTime = colTime + time
1102− -- Take the average of previous and the latest
1108+ -- XXX Take the average of previous and the latest
11031109 new =
11041110 if pendingCount > 0
11051111 then ((pendingTime `div` (fromIntegral pendingCount)) + prev)
src/Streamly/Streams/Ahead.hs+162−102
@@ -127,14 +127,104 @@ import Prelude hiding (map)
127127-- updates the heap count and the other updates the yield count.
128128-- we can pass that as an update function instead.
129129
130−workLoopAhead :: MonadIO m
130+processHeap :: MonadIO m
131131 => IORef ([Stream m a], Int)
132132 -> IORef (Heap (Entry Int (AheadHeapEntry Stream m a)) , Int)
133133 -> State Stream m a
134134 -> SVar Stream m a
135135 -> WorkerInfo
136+ -> AheadHeapEntry Stream m a
137+ -> Int
136138 -> m ()
137−workLoopAhead q heap st sv winfo = runHeap
139+processHeap q heap st sv winfo entry sno = loopHeap sno entry
140+
141+ where
142+
143+ loopHeap seqNo ent = do
144+#ifdef DIAGNOSTICS
145+ liftIO $ do
146+ maxHp <- readIORef (maxHeapSize sv)
147+ (hp, _) <- readIORef heap
148+ when (H.size hp > maxHp) $ writeIORef (maxHeapSize sv) (H.size hp)
149+#endif
150+ case ent of
151+ AheadEntryPure a -> do
152+ -- Use 'send' directly so that we do not account this in worker
153+ -- latency as this will not be the real latency.
154+ continue <- liftIO $ send sv (ChildYield a)
155+ if continue
156+ then nextHeap seqNo
157+ else stopHeap seqNo
158+ AheadEntryStream r -> runStreamWithYieldLimit True seqNo r
159+
160+ stopHeap seqNo = liftIO $ do
161+ atomicModifyIORef heap $ \(h, _) -> ((h, seqNo + 1), ())
162+ sendStop sv winfo
163+
164+ nextHeap prevSeqNo = do
165+ -- XXX use "dequeueIfSeqential prevSeqNo" instead of always
166+ -- updating the sequence number in heap.
167+ liftIO $ atomicModifyIORef heap $ \(h, _) -> ((h, prevSeqNo + 1), ())
168+ ent <- liftIO $ dequeueFromHeap heap
169+ case ent of
170+ Just (Entry seqNo hent) -> loopHeap seqNo hent
171+ Nothing -> do
172+ work <- dequeueAhead q
173+ case work of
174+ Nothing -> liftIO $ sendStop sv winfo
175+ Just (m, seqNo) -> do
176+ yieldLimitOk <- liftIO $ decrementYieldLimit sv
177+ if yieldLimitOk
178+ then do
179+ if seqNo == prevSeqNo + 1
180+ then processWithToken q heap st sv winfo m seqNo
181+ else processWithoutToken q heap st sv winfo m seqNo
182+ else liftIO $ do
183+ incrementYieldLimit sv
184+ sendStop sv winfo
185+
186+ singleStreamFromHeap seqNo a = do
187+ continue <- liftIO $ sendYield sv winfo (ChildYield a)
188+ if continue
189+ then nextHeap seqNo
190+ else stopHeap seqNo
191+
192+ runStreamWithYieldLimit continue seqNo r = do
193+ yieldLimitOk <- liftIO $ decrementYieldLimit sv
194+ if continue && yieldLimitOk
195+ then do
196+ let stop = do
197+ liftIO (incrementYieldLimit sv)
198+ nextHeap seqNo
199+ unStream r st stop
200+ (singleStreamFromHeap seqNo)
201+ (yieldStreamFromHeap seqNo)
202+ else liftIO $ do
203+ atomicModifyIORef heap $ \(h, _) ->
204+ ((H.insert (Entry seqNo (AheadEntryStream r)) h, seqNo), ())
205+ incrementYieldLimit sv
206+ sendStop sv winfo
207+
208+ yieldStreamFromHeap seqNo a r = do
209+ continue <- liftIO $ sendYield sv winfo (ChildYield a)
210+ runStreamWithYieldLimit continue seqNo r
211+
212+processWithoutToken :: MonadIO m
213+ => IORef ([Stream m a], Int)
214+ -> IORef (Heap (Entry Int (AheadHeapEntry Stream m a)) , Int)
215+ -> State Stream m a
216+ -> SVar Stream m a
217+ -> WorkerInfo
218+ -> Stream m a
219+ -> Int
220+ -> m ()
221+processWithoutToken q heap st sv winfo m sno = do
222+ -- we have already decremented the yield limit for m
223+ let stop = do
224+ liftIO (incrementYieldLimit sv)
225+ workLoopAhead q heap st sv winfo
226+
227+ unStream m st stop (singleToHeap sno) (yieldToHeap sno)
138228
139229 where
140230
@@ -170,7 +260,7 @@ workLoopAhead q heap st sv winfo = runHeap
170260 if heapOk
171261 then
172262 case yieldRateInfo sv of
173− Nothing -> runHeap
263+ Nothing -> workLoopAhead q heap st sv winfo
174264 Just yinfo -> do
175265 -- check the stop condition under a lock before actually
176266 -- stopping so that the whole herd does not stop at once.
@@ -179,7 +269,7 @@ workLoopAhead q heap st sv winfo = runHeap
179269 if rateOk
180270 then do
181271 liftIO $ putMVar (workerStopMVar yinfo) ()
182− runHeap
272+ workLoopAhead q heap st sv winfo
183273 else do
184274 liftIO $ sendStop sv winfo
185275 liftIO $ putMVar (workerStopMVar yinfo) ()
@@ -188,125 +278,103 @@ workLoopAhead q heap st sv winfo = runHeap
188278 singleToHeap seqNo a = toHeap seqNo (AheadEntryPure a)
189279 yieldToHeap seqNo a r = toHeap seqNo (AheadEntryStream (a `K.cons` r))
190280
281+processWithToken :: MonadIO m
282+ => IORef ([Stream m a], Int)
283+ -> IORef (Heap (Entry Int (AheadHeapEntry Stream m a)) , Int)
284+ -> State Stream m a
285+ -> SVar Stream m a
286+ -> WorkerInfo
287+ -> Stream m a
288+ -> Int
289+ -> m ()
290+processWithToken q heap st sv winfo action sno = do
291+ let stop = do
292+ liftIO (incrementYieldLimit sv)
293+ loopWithToken sno
294+
295+ unStream action st stop
296+ (singleOutput sno)
297+ (yieldOutput sno)
298+
299+ where
300+
191301 singleOutput seqNo a = do
192302 continue <- liftIO $ sendYield sv winfo (ChildYield a)
193303 if continue
194− then runQueueToken seqNo
304+ then loopWithToken seqNo
195305 else liftIO $ do
196306 atomicModifyIORef heap $ \(h, _) -> ((h, seqNo + 1), ())
197307 sendStop sv winfo
198308
199− singleFromHeap seqNo a = do
200− continue <- liftIO $ send sv (ChildYield a)
201− liftIO $ atomicModifyIORef heap $ \(h, _) -> ((h, seqNo + 1), ())
202− if continue
203− then runHeap
204− else liftIO $ sendStop sv winfo
205−
206− singleStreamFromHeap seqNo a = do
207− continue <- liftIO $ sendYield sv winfo (ChildYield a)
208− liftIO $ atomicModifyIORef heap $ \(h, _) -> ((h, seqNo + 1), ())
209− if continue
210− then runHeap
211− else liftIO $ sendStop sv winfo
212−
213309 -- XXX use a wrapper function around stop so that we never miss
214310 -- incrementing the yield in a stop continuation. Essentiatlly all
215311 -- "unstream" calls in this function must increment yield limit on stop.
216312 yieldOutput seqNo a r = do
217− continue <- liftIO $ sendYield sv winfo (ChildYield a)
218− yieldLimitOk <- liftIO $ decrementYieldLimit sv
219− if continue && yieldLimitOk
220− then do
221− let stop = liftIO (incrementYieldLimit sv) >> runQueueToken seqNo
222− unStream r st stop
223− (singleOutput seqNo)
224− (yieldOutput seqNo)
225− else liftIO $ do
226− atomicModifyIORef heap $ \(h, _) ->
227− ((H.insert (Entry seqNo (AheadEntryStream r)) h, seqNo), ())
228− incrementYieldLimit sv
229− sendStop sv winfo
230−
231− yieldStreamFromHeap seqNo a r = do
232313 continue <- liftIO $ sendYield sv winfo (ChildYield a)
233314 yieldLimitOk <- liftIO $ decrementYieldLimit sv
234315 if continue && yieldLimitOk
235316 then do
236317 let stop = do
237− liftIO $ atomicModifyIORef heap $ \(h, _) ->
238− ((h, seqNo + 1), ())
239318 liftIO (incrementYieldLimit sv)
240− runHeap
319+ loopWithToken seqNo
241320 unStream r st stop
242− (singleStreamFromHeap seqNo)
243− (yieldStreamFromHeap seqNo)
321+ (singleOutput seqNo)
322+ (yieldOutput seqNo)
244323 else liftIO $ do
245324 atomicModifyIORef heap $ \(h, _) ->
246− ((H.insert (Entry seqNo (AheadEntryStream r)) h, seqNo), ())
325+ ((H.insert (Entry seqNo (AheadEntryStream r)) h, seqNo), ())
247326 incrementYieldLimit sv
248327 sendStop sv winfo
249328
250− {-# INLINE runQueueToken #-}
251− runQueueToken prevSeqNo = do
329+ loopWithToken prevSeqNo = do
252330 work <- dequeueAhead q
253331 case work of
254332 Nothing -> do
255333 liftIO $ atomicModifyIORef heap $ \(h, _) ->
256334 ((h, prevSeqNo + 1), ())
257− runHeap
335+
336+ ent <- liftIO $ dequeueFromHeap heap
337+ case ent of
338+ Just (Entry seqNo hent) ->
339+ processHeap q heap st sv winfo hent seqNo
340+ Nothing -> liftIO $ sendStop sv winfo
341+
258342 Just (m, seqNo) -> do
259343 yieldLimitOk <- liftIO $ decrementYieldLimit sv
260344 if yieldLimitOk
261345 then do
262346 if seqNo == prevSeqNo + 1
263347 then do
264− let stop = liftIO (incrementYieldLimit sv)
265− >> runQueueToken seqNo
348+ let stop = do
349+ liftIO (incrementYieldLimit sv)
350+ loopWithToken seqNo
266351 unStream m st stop
267352 (singleOutput seqNo)
268353 (yieldOutput seqNo)
269354 else do
270355 liftIO $ atomicModifyIORef heap $ \(h, _) ->
271− ((h, prevSeqNo + 1), ())
272− let stop = liftIO (incrementYieldLimit sv)
273− >> runHeap
274− unStream m st stop
275− (singleToHeap seqNo)
276− (yieldToHeap seqNo)
356+ ((h, prevSeqNo + 1), ())
357+ -- XXX put it back on the queue and start with the Heap?
358+ processWithoutToken q heap st sv winfo m seqNo
277359 else liftIO $ do
278360 atomicModifyIORef heap $ \(h, _) ->
279− ((h, prevSeqNo + 1), ())
361+ ((h, prevSeqNo + 1), ())
280362 incrementYieldLimit sv
281363 sendStop sv winfo
282364
283− runQueueNoToken = do
284− work <- dequeueAhead q
285− case work of
286− Nothing -> runHeap
287− Just (m, seqNo) -> do
288− yieldLimitOk <- liftIO $ decrementYieldLimit sv
289− if yieldLimitOk
290− then do
291− if seqNo == 0
292− then do
293− let stop = liftIO (incrementYieldLimit sv)
294− >> runQueueToken seqNo
295− unStream m st stop
296− (singleOutput seqNo)
297− (yieldOutput seqNo)
298− else do
299− let stop = liftIO (incrementYieldLimit sv)
300− >> runHeap
301− unStream m st stop
302− (singleToHeap seqNo)
303− (yieldToHeap seqNo)
304− else liftIO $ do
305− incrementYieldLimit sv
306− sendStop sv winfo
307−
308− {-# NOINLINE runHeap #-}
309− runHeap = do
365+-- XXX the yield limit changes increased the performance overhead by 30-40%.
366+-- Just like AsyncT we can use an implementation without yeidlimit and even
367+-- without pacing code to keep the performance higher in the unlimited and
368+-- unpaced case.
369+{-# INLINE workLoopAhead #-}
370+workLoopAhead :: MonadIO m
371+ => IORef ([Stream m a], Int)
372+ -> IORef (Heap (Entry Int (AheadHeapEntry Stream m a)) , Int)
373+ -> State Stream m a
374+ -> SVar Stream m a
375+ -> WorkerInfo
376+ -> m ()
377+workLoopAhead q heap st sv winfo = do
310378#ifdef DIAGNOSTICS
311379 liftIO $ do
312380 maxHp <- readIORef (maxHeapSize sv)
@@ -316,7 +384,7 @@ workLoopAhead q heap st sv winfo = runHeap
316384 ent <- liftIO $ dequeueFromHeap heap
317385 case ent of
318386 Nothing -> do
319− -- Before we pick up the next item from the work queue we check
387+ -- Before we execute the next item from the work queue we check
320388 -- if we are beyond the yield limit. It is better to check the
321389 -- yield limit before we pick up the next item. Otherwise we
322390 -- may have already started more tasks even though we may have
@@ -325,8 +393,6 @@ workLoopAhead q heap st sv winfo = runHeap
325393 -- workers may go away without picking up work and yielding a
326394 -- value.
327395 --
328− -- XXX do the same for Async stream as well.
329− --
330396 -- Rate control can be done either based on actual yields in
331397 -- the output queue or based on any yield either to the heap or
332398 -- to the output queue. In both cases we may have one issue or
@@ -334,26 +400,20 @@ workLoopAhead q heap st sv winfo = runHeap
334400 -- output queue because it makes the code common to both async
335401 -- and ahead streams.
336402 --
337− done <- queueEmptyAhead q
338− if done
339− -- XXX need to run heap here if not empty
340− then liftIO $ sendStop sv winfo
341− else runQueueNoToken
342− Just (Entry seqNo hent) -> do
343− case hent of
344− -- Do not account this in worker latency as this will not
345− -- be the real latency.
346− AheadEntryPure a -> singleFromHeap seqNo a
347− AheadEntryStream r -> do
348− let stp = do
349− liftIO $ atomicModifyIORef heap $ \(h, _) ->
350− ((h, seqNo + 1), ())
351− liftIO (incrementYieldLimit sv)
352− runHeap
353−
354− unStream r st stp
355− (singleStreamFromHeap seqNo)
356− (yieldStreamFromHeap seqNo)
403+ work <- dequeueAhead q
404+ case work of
405+ Nothing -> liftIO $ sendStop sv winfo
406+ Just (m, seqNo) -> do
407+ yieldLimitOk <- liftIO $ decrementYieldLimit sv
408+ if yieldLimitOk
409+ then do
410+ if seqNo == 0
411+ then processWithToken q heap st sv winfo m seqNo
412+ else processWithoutToken q heap st sv winfo m seqNo
413+ else liftIO $ do
414+ incrementYieldLimit sv
415+ sendStop sv winfo
416+ Just (Entry seqNo hent) -> processHeap q heap st sv winfo hent seqNo
357417
358418-------------------------------------------------------------------------------
359419-- WAhead
src/Streamly/Streams/Async.hs+120−12
@@ -77,6 +77,45 @@ workLoopLIFO q st sv winfo = run
7777
7878 where
7979
80+ run = do
81+ work <- dequeue
82+ case work of
83+ Nothing -> liftIO $ sendStop sv winfo
84+ Just m -> unStream m st run single yieldk
85+
86+ single a = do
87+ res <- liftIO $ sendYield sv winfo (ChildYield a)
88+ if res then run else liftIO $ sendStop sv winfo
89+
90+ yieldk a r = do
91+ res <- liftIO $ sendYield sv winfo (ChildYield a)
92+ if res
93+ then unStream r st run single yieldk
94+ else liftIO $ do
95+ enqueueLIFO sv q r
96+ sendStop sv winfo
97+
98+ dequeue = liftIO $ atomicModifyIORefCAS q $ \case
99+ [] -> ([], Nothing)
100+ x : xs -> (xs, Just x)
101+
102+-- We duplicate workLoop for yield limit and no limit cases because it has
103+-- around 40% performance overhead in the worst case.
104+--
105+-- XXX we can pass yinfo directly as an argument here so that we do not have to
106+-- make a check every time.
107+{-# INLINE workLoopLIFOLimited #-}
108+workLoopLIFOLimited
109+ :: MonadIO m
110+ => IORef [Stream m a]
111+ -> State Stream m a
112+ -> SVar Stream m a
113+ -> WorkerInfo
114+ -> m ()
115+workLoopLIFOLimited q st sv winfo = run
116+
117+ where
118+
80119 run = do
81120 work <- dequeue
82121 case work of
@@ -139,6 +178,36 @@ workLoopFIFO q st sv winfo = run
139178
140179 where
141180
181+ run = do
182+ work <- liftIO $ tryPopR q
183+ case work of
184+ Nothing -> liftIO $ sendStop sv winfo
185+ Just m -> unStream m st run single yieldk
186+
187+ single a = do
188+ res <- liftIO $ sendYield sv winfo (ChildYield a)
189+ if res then run else liftIO $ sendStop sv winfo
190+
191+ yieldk a r = do
192+ res <- liftIO $ sendYield sv winfo (ChildYield a)
193+ if res
194+ then unStream r st run single yieldk
195+ else liftIO $ do
196+ enqueueFIFO sv q r
197+ sendStop sv winfo
198+
199+{-# INLINE workLoopFIFOLimited #-}
200+workLoopFIFOLimited
201+ :: MonadIO m
202+ => LinkedQueue (Stream m a)
203+ -> State Stream m a
204+ -> SVar Stream m a
205+ -> WorkerInfo
206+ -> m ()
207+workLoopFIFOLimited q st sv winfo = run
208+
209+ where
210+
142211 run = do
143212 work <- liftIO $ tryPopR q
144213 case work of
@@ -197,7 +266,9 @@ getLifoSVar st = do
197266 maxHs <- newIORef 0
198267 maxWq <- newIORef 0
199268#endif
200− let isWorkFinished sv = do
269+ let isWorkFinished _ = null <$> readIORef q
270+
271+ let isWorkFinishedLimited sv = do
201272 yieldsDone <-
202273 case remainingYields sv of
203274 Just ref -> do
@@ -207,7 +278,7 @@ getLifoSVar st = do
207278 qEmpty <- null <$> readIORef q
208279 return $ qEmpty || yieldsDone
209280
210− let getSVar sv readOutput postProc = SVar
281+ let getSVar sv readOutput postProc workDone wloop = SVar
211282 { outputQueue = outQ
212283 , remainingYields = yl
213284 , maxBufferLimit = getMaxBuffer st
@@ -217,9 +288,9 @@ getLifoSVar st = do
217288 , readOutputQ = readOutput sv
218289 , postProcess = postProc sv
219290 , workerThreads = running
220− , workLoop = workLoopLIFO q st{streamVar = Just sv} sv
291+ , workLoop = wloop q st{streamVar = Just sv} sv
221292 , enqueue = enqueueLIFO sv q
222− , isWorkDone = isWorkFinished sv
293+ , isWorkDone = workDone sv
223294 , needDoorBell = wfw
224295 , svarStyle = AsyncVar
225296 , workerCount = active
@@ -237,8 +308,26 @@ getLifoSVar st = do
237308
238309 let sv =
239310 case getMaxStreamRate st of
240− Nothing -> getSVar sv readOutputQBounded postProcessBounded
241− Just _ -> getSVar sv readOutputQPaced postProcessPaced
311+ Nothing ->
312+ case getYieldLimit st of
313+ Nothing -> getSVar sv readOutputQBounded
314+ postProcessBounded
315+ isWorkFinished
316+ workLoopLIFO
317+ Just _ -> getSVar sv readOutputQBounded
318+ postProcessBounded
319+ isWorkFinishedLimited
320+ workLoopLIFOLimited
321+ Just _ ->
322+ case getYieldLimit st of
323+ Nothing -> getSVar sv readOutputQPaced
324+ postProcessPaced
325+ isWorkFinished
326+ workLoopLIFO
327+ Just _ -> getSVar sv readOutputQPaced
328+ postProcessPaced
329+ isWorkFinishedLimited
330+ workLoopLIFOLimited
242331 in return sv
243332
244333getFifoSVar :: MonadAsync m => State Stream m a -> IO (SVar Stream m a)
@@ -262,7 +351,8 @@ getFifoSVar st = do
262351 maxWq <- newIORef 0
263352#endif
264353
265− let isWorkFinished sv = do
354+ let isWorkFinished _ = nullQ q
355+ let isWorkFinishedLimited sv = do
266356 yieldsDone <-
267357 case remainingYields sv of
268358 Just ref -> do
@@ -272,7 +362,7 @@ getFifoSVar st = do
272362 qEmpty <- nullQ q
273363 return $ qEmpty || yieldsDone
274364
275− let getSVar sv readOutput postProc = SVar
365+ let getSVar sv readOutput postProc workDone wloop = SVar
276366 { outputQueue = outQ
277367 , remainingYields = yl
278368 , maxBufferLimit = getMaxBuffer st
@@ -282,9 +372,9 @@ getFifoSVar st = do
282372 , readOutputQ = readOutput sv
283373 , postProcess = postProc sv
284374 , workerThreads = running
285− , workLoop = workLoopFIFO q st{streamVar = Just sv} sv
375+ , workLoop = wloop q st{streamVar = Just sv} sv
286376 , enqueue = enqueueFIFO sv q
287− , isWorkDone = isWorkFinished sv
377+ , isWorkDone = workDone sv
288378 , needDoorBell = wfw
289379 , svarStyle = WAsyncVar
290380 , workerCount = active
@@ -302,8 +392,26 @@ getFifoSVar st = do
302392
303393 let sv =
304394 case getMaxStreamRate st of
305− Nothing -> getSVar sv readOutputQBounded postProcessBounded
306− Just _ -> getSVar sv readOutputQPaced postProcessPaced
395+ Nothing ->
396+ case getYieldLimit st of
397+ Nothing -> getSVar sv readOutputQBounded
398+ postProcessBounded
399+ isWorkFinished
400+ workLoopFIFO
401+ Just _ -> getSVar sv readOutputQBounded
402+ postProcessBounded
403+ isWorkFinishedLimited
404+ workLoopFIFOLimited
405+ Just _ ->
406+ case getYieldLimit st of
407+ Nothing -> getSVar sv readOutputQPaced
408+ postProcessPaced
409+ isWorkFinished
410+ workLoopFIFO
411+ Just _ -> getSVar sv readOutputQPaced
412+ postProcessPaced
413+ isWorkFinishedLimited
414+ workLoopFIFOLimited
307415 in return sv
308416
309417{-# INLINABLE newAsyncVar #-}
src/Streamly/Streams/SVar.hs+2−5
@@ -78,11 +78,8 @@ fromStreamVar sv = Stream $ \st stp sng yld -> do
7878 processEvents (ev : es) = Stream $ \st stp sng yld -> do
7979 let rest = processEvents es
8080 case ev of
81− ChildYield a -> do
82−-- void $ dispatchWorkerPaced sv
83− yld a rest
81+ ChildYield a -> yld a rest
8482 ChildStop tid e -> do
85− -- void $ dispatchWorkerPaced sv
8683 accountThread sv tid
8784 case e of
8885 Nothing -> unStream rest (rstState st) stp sng yld
@@ -104,7 +101,7 @@ toSVar sv m = toStreamVar sv (toStream m)
104101-- XXX need to write these in direct style otherwise they will break fusion.
105102--
106103-- | Specify the maximum number of threads that can be spawned concurrently
107−-- when using concurrent streams. This values denotes maximum concurrent
104+-- when using concurrent streams. This value denotes maximum concurrent
108105-- requests, or tasks in progress at any given point of time. Note that this is
109106-- not the grand total number of threads but maximum threads at an individual
110107-- point of concurrency.
test/MaxRate.hs+2−3
@@ -52,7 +52,7 @@ measureRate' desc t rate consumerDelay producerDelay dur = do
5252 else randomRIO ( round $ toMicroSecs t1
5353 , round $ toMicroSecs t2)
5454 when (r > 0) $ do
55− t1 <- getTime Monotonic
55+ -- t1 <- getTime Monotonic
5656 threadDelay r
5757 -- t2 <- getTime Monotonic
5858 -- let delta = fromIntegral (toNanoSecs (t2 - t1)) / 1000000000
@@ -87,7 +87,6 @@ main = hspec $ do
8787 let rates = [1, 10, 100, 1000, 10000, 25000]
8888 in describe "asyncly no consumer delay and 1 sec producer delay" $ do
8989 forM_ rates (\r -> measureRate "asyncly" asyncly r 0 1 range)
90− -}
9190
9291 let rates = [1, 10, 100, 1000, 10000, 25000]
9392 in describe "asyncly no consumer delay and variable producer delay" $ do
@@ -102,7 +101,7 @@ main = hspec $ do
102101 in describe "wAsyncly no consumer delay and 1 sec producer delay" $ do
103102 forM_ rates (\r -> measureRate "wAsyncly" wAsyncly r 0 1 range)
104103
105− let rates = [1, 10, 100, 1000, 10000, 100000]
104+ let rates = [1, 10, 100, 1000, 10000, 100000, 1000000]
106105 in describe "aheadly no consumer delay no producer delay" $ do
107106 forM_ rates (\r -> measureRate "aheadly" aheadly r 0 0 range)
108107