@@ -127,14 +127,104 @@ import Prelude hiding (map)
127-- updates the heap count and the other updates the yield count.
127-- updates the heap count and the other updates the yield count.
128-- we can pass that as an update function instead.
128-- we can pass that as an update function instead.
130−workLoopAhead :: MonadIO m
130+processHeap :: MonadIO m
131 => IORef ([Stream m a], Int)
131 => IORef ([Stream m a], Int)
132 -> IORef (Heap (Entry Int (AheadHeapEntry Stream m a)) , Int)
132 -> IORef (Heap (Entry Int (AheadHeapEntry Stream m a)) , Int)
133 -> State Stream m a
133 -> State Stream m a
134 -> SVar Stream m a
134 -> SVar Stream m a
135 -> WorkerInfo
135 -> WorkerInfo
136+ -> AheadHeapEntry Stream m a
139+processHeap q heap st sv winfo entry sno = loopHeap sno entry
143+ loopHeap seqNo ent = do
146+ maxHp <- readIORef (maxHeapSize sv)
147+ (hp, _) <- readIORef heap
148+ when (H.size hp > maxHp) $ writeIORef (maxHeapSize sv) (H.size hp)
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)
158+ AheadEntryStream r -> runStreamWithYieldLimit True seqNo r
160+ stopHeap seqNo = liftIO $ do
161+ atomicModifyIORef heap $ \(h, _) -> ((h, seqNo + 1), ())
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
170+ Just (Entry seqNo hent) -> loopHeap seqNo hent
172+ work <- dequeueAhead q
174+ Nothing -> liftIO $ sendStop sv winfo
175+ Just (m, seqNo) -> do
176+ yieldLimitOk <- liftIO $ decrementYieldLimit sv
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
183+ incrementYieldLimit sv
186+ singleStreamFromHeap seqNo a = do
187+ continue <- liftIO $ sendYield sv winfo (ChildYield a)
192+ runStreamWithYieldLimit continue seqNo r = do
193+ yieldLimitOk <- liftIO $ decrementYieldLimit sv
194+ if continue && yieldLimitOk
197+ liftIO (incrementYieldLimit sv)
200+ (singleStreamFromHeap seqNo)
201+ (yieldStreamFromHeap seqNo)
203+ atomicModifyIORef heap $ \(h, _) ->
204+ ((H.insert (Entry seqNo (AheadEntryStream r)) h, seqNo), ())
205+ incrementYieldLimit sv
208+ yieldStreamFromHeap seqNo a r = do
209+ continue <- liftIO $ sendYield sv winfo (ChildYield a)
210+ runStreamWithYieldLimit continue seqNo r
212+processWithoutToken :: MonadIO m
213+ => IORef ([Stream m a], Int)
214+ -> IORef (Heap (Entry Int (AheadHeapEntry Stream m a)) , Int)
221+processWithoutToken q heap st sv winfo m sno = do
222+ -- we have already decremented the yield limit for m
224+ liftIO (incrementYieldLimit sv)
137−workLoopAhead q heap st sv winfo = runHeap
225+ workLoopAhead q heap st sv winfo
227+ unStream m st stop (singleToHeap sno) (yieldToHeap sno)
@@ -170,7 +260,7 @@ workLoopAhead q heap st sv winfo = runHeap
170 if heapOk
260 if heapOk
172 case yieldRateInfo sv of
262 case yieldRateInfo sv of
173− Nothing -> runHeap
263+ Nothing -> workLoopAhead q heap st sv winfo
174 Just yinfo -> do
264 Just yinfo -> do
175 -- check the stop condition under a lock before actually
265 -- check the stop condition under a lock before actually
176 -- stopping so that the whole herd does not stop at once.
266 -- stopping so that the whole herd does not stop at once.
@@ -179,7 +269,7 @@ workLoopAhead q heap st sv winfo = runHeap
179 if rateOk
269 if rateOk
181 liftIO $ putMVar (workerStopMVar yinfo) ()
271 liftIO $ putMVar (workerStopMVar yinfo) ()
182− runHeap
272+ workLoopAhead q heap st sv winfo
184 liftIO $ sendStop sv winfo
274 liftIO $ sendStop sv winfo
185 liftIO $ putMVar (workerStopMVar yinfo) ()
275 liftIO $ putMVar (workerStopMVar yinfo) ()
@@ -188,125 +278,103 @@ workLoopAhead q heap st sv winfo = runHeap
188 singleToHeap seqNo a = toHeap seqNo (AheadEntryPure a)
278 singleToHeap seqNo a = toHeap seqNo (AheadEntryPure a)
189 yieldToHeap seqNo a r = toHeap seqNo (AheadEntryStream (a `K.cons` r))
279 yieldToHeap seqNo a r = toHeap seqNo (AheadEntryStream (a `K.cons` r))
281+processWithToken :: MonadIO m
282+ => IORef ([Stream m a], Int)
283+ -> IORef (Heap (Entry Int (AheadHeapEntry Stream m a)) , Int)
290+processWithToken q heap st sv winfo action sno = do
292+ liftIO (incrementYieldLimit sv)
295+ unStream action st stop
191 singleOutput seqNo a = do
301 singleOutput seqNo a = do
192 continue <- liftIO $ sendYield sv winfo (ChildYield a)
302 continue <- liftIO $ sendYield sv winfo (ChildYield a)
193 if continue
303 if continue
194− then runQueueToken seqNo
304+ then loopWithToken seqNo
195 else liftIO $ do
305 else liftIO $ do
196 atomicModifyIORef heap $ \(h, _) -> ((h, seqNo + 1), ())
306 atomicModifyIORef heap $ \(h, _) -> ((h, seqNo + 1), ())
197 sendStop sv winfo
307 sendStop sv winfo
199− singleFromHeap seqNo a = do
200− continue <- liftIO $ send sv (ChildYield a)
201− liftIO $ atomicModifyIORef heap $ \(h, _) -> ((h, seqNo + 1), ())
204− else liftIO $ sendStop sv winfo
206− singleStreamFromHeap seqNo a = do
207− continue <- liftIO $ sendYield sv winfo (ChildYield a)
208− liftIO $ atomicModifyIORef heap $ \(h, _) -> ((h, seqNo + 1), ())
211− else liftIO $ sendStop sv winfo
213 -- XXX use a wrapper function around stop so that we never miss
309 -- XXX use a wrapper function around stop so that we never miss
214 -- incrementing the yield in a stop continuation. Essentiatlly all
310 -- incrementing the yield in a stop continuation. Essentiatlly all
215 -- "unstream" calls in this function must increment yield limit on stop.
311 -- "unstream" calls in this function must increment yield limit on stop.
216 yieldOutput seqNo a r = do
312 yieldOutput seqNo a r = do
217− continue <- liftIO $ sendYield sv winfo (ChildYield a)
218− yieldLimitOk <- liftIO $ decrementYieldLimit sv
219− if continue && yieldLimitOk
221− let stop = liftIO (incrementYieldLimit sv) >> runQueueToken seqNo
223− (singleOutput seqNo)
226− atomicModifyIORef heap $ \(h, _) ->
227− ((H.insert (Entry seqNo (AheadEntryStream r)) h, seqNo), ())
228− incrementYieldLimit sv
231− yieldStreamFromHeap seqNo a r = do
232 continue <- liftIO $ sendYield sv winfo (ChildYield a)
313 continue <- liftIO $ sendYield sv winfo (ChildYield a)
233 yieldLimitOk <- liftIO $ decrementYieldLimit sv
314 yieldLimitOk <- liftIO $ decrementYieldLimit sv
234 if continue && yieldLimitOk
315 if continue && yieldLimitOk
236 let stop = do
317 let stop = do
237− liftIO $ atomicModifyIORef heap $ \(h, _) ->
238− ((h, seqNo + 1), ())
239 liftIO (incrementYieldLimit sv)
318 liftIO (incrementYieldLimit sv)
240− runHeap
319+ loopWithToken seqNo
241 unStream r st stop
320 unStream r st stop
242− (singleStreamFromHeap seqNo)
321+ (singleOutput seqNo)
243− (yieldStreamFromHeap seqNo)
322+ (yieldOutput seqNo)
244 else liftIO $ do
323 else liftIO $ do
245 atomicModifyIORef heap $ \(h, _) ->
324 atomicModifyIORef heap $ \(h, _) ->
246− ((H.insert (Entry seqNo (AheadEntryStream r)) h, seqNo), ())
325+ ((H.insert (Entry seqNo (AheadEntryStream r)) h, seqNo), ())
247 incrementYieldLimit sv
326 incrementYieldLimit sv
248 sendStop sv winfo
327 sendStop sv winfo
250− {-# INLINE runQueueToken #-}
251− runQueueToken prevSeqNo = do
329+ loopWithToken prevSeqNo = do
252 work <- dequeueAhead q
330 work <- dequeueAhead q
253 case work of
331 case work of
254 Nothing -> do
332 Nothing -> do
255 liftIO $ atomicModifyIORef heap $ \(h, _) ->
333 liftIO $ atomicModifyIORef heap $ \(h, _) ->
256 ((h, prevSeqNo + 1), ())
334 ((h, prevSeqNo + 1), ())
336+ ent <- liftIO $ dequeueFromHeap heap
338+ Just (Entry seqNo hent) ->
339+ processHeap q heap st sv winfo hent seqNo
340+ Nothing -> liftIO $ sendStop sv winfo
258 Just (m, seqNo) -> do
342 Just (m, seqNo) -> do
259 yieldLimitOk <- liftIO $ decrementYieldLimit sv
343 yieldLimitOk <- liftIO $ decrementYieldLimit sv
260 if yieldLimitOk
344 if yieldLimitOk
262 if seqNo == prevSeqNo + 1
346 if seqNo == prevSeqNo + 1
264− let stop = liftIO (incrementYieldLimit sv)
349+ liftIO (incrementYieldLimit sv)
265− >> runQueueToken seqNo
350+ loopWithToken seqNo
266 unStream m st stop
351 unStream m st stop
267 (singleOutput seqNo)
352 (singleOutput seqNo)
268 (yieldOutput seqNo)
353 (yieldOutput seqNo)
270 liftIO $ atomicModifyIORef heap $ \(h, _) ->
355 liftIO $ atomicModifyIORef heap $ \(h, _) ->
271− ((h, prevSeqNo + 1), ())
356+ ((h, prevSeqNo + 1), ())
272− let stop = liftIO (incrementYieldLimit sv)
357+ -- XXX put it back on the queue and start with the Heap?
273− >> runHeap
358+ processWithoutToken q heap st sv winfo m seqNo
275− (singleToHeap seqNo)
277 else liftIO $ do
359 else liftIO $ do
278 atomicModifyIORef heap $ \(h, _) ->
360 atomicModifyIORef heap $ \(h, _) ->
279− ((h, prevSeqNo + 1), ())
361+ ((h, prevSeqNo + 1), ())
280 incrementYieldLimit sv
362 incrementYieldLimit sv
281 sendStop sv winfo
363 sendStop sv winfo
283− runQueueNoToken = do
365+-- XXX the yield limit changes increased the performance overhead by 30-40%.
284− work <- dequeueAhead q
366+-- Just like AsyncT we can use an implementation without yeidlimit and even
285− case work of
367+-- without pacing code to keep the performance higher in the unlimited and
286− Nothing -> runHeap
368+-- unpaced case.
287− Just (m, seqNo) -> do
369+{-# INLINE workLoopAhead #-}
288− yieldLimitOk <- liftIO $ decrementYieldLimit sv
370+workLoopAhead :: MonadIO m
289− if yieldLimitOk
371+ => IORef ([Stream m a], Int)
290− then do
372+ -> IORef (Heap (Entry Int (AheadHeapEntry Stream m a)) , Int)
291− if seqNo == 0
373+ -> State Stream m a
292− then do
374+ -> SVar Stream m a
293− let stop = liftIO (incrementYieldLimit sv)
375+ -> WorkerInfo
294− >> runQueueToken seqNo
376+ -> m ()
295− unStream m st stop
377+workLoopAhead q heap st sv winfo = do
296− (singleOutput seqNo)
299− let stop = liftIO (incrementYieldLimit sv)
302− (singleToHeap seqNo)
305− incrementYieldLimit sv
308− {-# NOINLINE runHeap #-}
310#ifdef DIAGNOSTICS
378#ifdef DIAGNOSTICS
311 liftIO $ do
379 liftIO $ do
312 maxHp <- readIORef (maxHeapSize sv)
380 maxHp <- readIORef (maxHeapSize sv)
@@ -316,7 +384,7 @@ workLoopAhead q heap st sv winfo = runHeap
316 ent <- liftIO $ dequeueFromHeap heap
384 ent <- liftIO $ dequeueFromHeap heap
317 case ent of
385 case ent of
318 Nothing -> do
386 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
320 -- if we are beyond the yield limit. It is better to check the
388 -- if we are beyond the yield limit. It is better to check the
321 -- yield limit before we pick up the next item. Otherwise we
389 -- yield limit before we pick up the next item. Otherwise we
322 -- may have already started more tasks even though we may have
390 -- may have already started more tasks even though we may have
@@ -325,8 +393,6 @@ workLoopAhead q heap st sv winfo = runHeap
325 -- workers may go away without picking up work and yielding a
393 -- workers may go away without picking up work and yielding a
326 -- value.
394 -- value.
328− -- XXX do the same for Async stream as well.
330 -- Rate control can be done either based on actual yields in
396 -- Rate control can be done either based on actual yields in
331 -- the output queue or based on any yield either to the heap or
397 -- the output queue or based on any yield either to the heap or
332 -- to the output queue. In both cases we may have one issue or
398 -- to the output queue. In both cases we may have one issue or
@@ -334,26 +400,20 @@ workLoopAhead q heap st sv winfo = runHeap
334 -- output queue because it makes the code common to both async
400 -- output queue because it makes the code common to both async
335 -- and ahead streams.
401 -- and ahead streams.
337− done <- queueEmptyAhead q
403+ work <- dequeueAhead q
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
404+ case work of
344− -- Do not account this in worker latency as this will not
405+ Nothing -> liftIO $ sendStop sv winfo
345− -- be the real latency.
406+ Just (m, seqNo) -> do
346− AheadEntryPure a -> singleFromHeap seqNo a
407+ yieldLimitOk <- liftIO $ decrementYieldLimit sv
347− AheadEntryStream r -> do
408+ if yieldLimitOk
348− let stp = do
409+ then do
349− liftIO $ atomicModifyIORef heap $ \(h, _) ->
410+ if seqNo == 0
350− ((h, seqNo + 1), ())
411+ then processWithToken q heap st sv winfo m seqNo
412+ else processWithoutToken q heap st sv winfo m seqNo
351− liftIO (incrementYieldLimit sv)
414+ incrementYieldLimit sv
352− runHeap
415+ sendStop sv winfo
353−
416+ Just (Entry seqNo hent) -> processHeap q heap st sv winfo hent seqNo
355− (singleStreamFromHeap seqNo)
356− (yieldStreamFromHeap seqNo)
358-------------------------------------------------------------------------------
418-------------------------------------------------------------------------------