@@ -20,6 +20,10 @@
20-- Portability : GHC
20-- Portability : GHC
23+#ifdef DIAGNOSTICS_VERBOSE
23module Streamly.SVar
27module Streamly.SVar
25 MonadAsync
29 MonadAsync
@@ -80,8 +84,9 @@ module Streamly.SVar
80 , delThread
84 , delThread
82 , toStreamVar
86 , toStreamVar
84 , SVarStats (..)
87 , SVarStats (..)
85 , dumpSVar
90 , dumpSVar
@@ -127,7 +132,6 @@ import Control.Concurrent.MVar (tryTakeMVar)
127import Control.Exception
132import Control.Exception
128 (catches, throwIO, Handler(..), BlockedIndefinitelyOnMVar(..),
133 (catches, throwIO, Handler(..), BlockedIndefinitelyOnMVar(..),
129 BlockedIndefinitelyOnSTM(..))
134 BlockedIndefinitelyOnSTM(..))
130−import Data.Maybe (isJust)
131import System.IO (hPutStrLn, stderr)
135import System.IO (hPutStrLn, stderr)
@@ -264,18 +268,17 @@ data YieldRateInfo = YieldRateInfo
264 , workerStopTimeStamp :: IORef TimeSpec
268 , workerStopTimeStamp :: IORef TimeSpec
268data SVarStats = SVarStats {
271data SVarStats = SVarStats {
269− totalDispatches :: IORef Int
272+ totalDispatches :: IORef Int
270− , maxWorkers :: IORef Int
273+ , maxWorkers :: IORef Int
271− , maxOutQSize :: IORef Int
274+ , maxOutQSize :: IORef Int
272− , maxHeapSize :: IORef Int
275+ , maxHeapSize :: IORef Int
273− , maxWorkQSize :: IORef Int
276+ , maxWorkQSize :: IORef Int
277+ , avgWorkerLatency :: IORef (Count, NanoSecs)
274− -- , avgLatency :: IORef NanoSecs
278+ , minWorkerLatency :: IORef NanoSecs
275− -- , minLatency :: IORef NanoSecs
279+ , maxWorkerLatency :: IORef NanoSecs
276− -- , maxLatency :: IORef NanoSecs
280+ , svarStopTime :: IORef (Maybe TimeSpec)
280data Limit = Unlimited | Limited Word deriving Show
283data Limit = Unlimited | Limited Word deriving Show
@@ -308,13 +311,13 @@ data SVar t m a = SVar
308 , accountThread :: ThreadId -> m ()
311 , accountThread :: ThreadId -> m ()
309 , workerStopMVar :: MVar ()
312 , workerStopMVar :: MVar ()
314+ , svarStats :: SVarStats
311#ifdef DIAGNOSTICS
315#ifdef DIAGNOSTICS
312 -- to track garbage collection of SVar
316 -- to track garbage collection of SVar
313 , svarRef :: Maybe (IORef ())
317 , svarRef :: Maybe (IORef ())
314 , outputHeap :: IORef (Heap (Entry Int (AheadHeapEntry t m a)) , Int)
318 , outputHeap :: IORef (Heap (Entry Int (AheadHeapEntry t m a)) , Int)
315 -- Shared work queue (stream, seqNo)
319 -- Shared work queue (stream, seqNo)
316 , aheadWorkQueue :: IORef ([t m a], Int)
320 , aheadWorkQueue :: IORef ([t m a], Int)
317− , svarStats :: SVarStats
@@ -456,20 +459,58 @@ getStreamLatency = _streamLatency
456-------------------------------------------------------------------------------
459-------------------------------------------------------------------------------
458#ifdef DIAGNOSTICS
461#ifdef DIAGNOSTICS
459−dumpSVarStats :: SVarStats -> SVarStyle -> Bool -> IO String
462+dumpSVarStats :: SVar t m a -> SVarStats -> SVarStyle -> IO String
460−dumpSVarStats ss style _paced = do
463+dumpSVarStats sv ss style = do
464+ case yieldRateInfo sv of
465+ Nothing -> return ()
467+ _ <- liftIO $ collectLatency (svarStats sv) yinfo
461 dispatches <- readIORef $ totalDispatches ss
470 dispatches <- readIORef $ totalDispatches ss
462 maxWrk <- readIORef $ maxWorkers ss
471 maxWrk <- readIORef $ maxWorkers ss
463 maxOq <- readIORef $ maxOutQSize ss
472 maxOq <- readIORef $ maxOutQSize ss
464 maxHp <- readIORef $ maxHeapSize ss
473 maxHp <- readIORef $ maxHeapSize ss
474+ minLat <- readIORef $ minWorkerLatency ss
475+ maxLat <- readIORef $ maxWorkerLatency ss
476+ (avgCnt, avgTime) <- readIORef $ avgWorkerLatency ss
477+ svarLat <- case yieldRateInfo sv of
480+ (cnt, startTime) <- readIORef $ workerLongTermLatency yinfo
483+ t <- readIORef (svarStopTime ss)
486+ now <- getTime Monotonic
487+ let interval = toNanoSecs (now - startTime)
488+ return $ interval `div` fromIntegral cnt
490+ let interval = toNanoSecs (stopTime - startTime)
491+ return $ interval `div` fromIntegral cnt
466 return $ unlines
494 return $ unlines
467 [ "total dispatches = " ++ show dispatches
495 [ "total dispatches = " ++ show dispatches
468 , "max workers = " ++ show maxWrk
496 , "max workers = " ++ show maxWrk
469 , "max outQSize = " ++ show maxOq
497 , "max outQSize = " ++ show maxOq
470− , if style == AheadVar
498+ ++ (if style == AheadVar
471− then "heap max size = " ++ show maxHp
499+ then "\nheap max size = " ++ show maxHp
502+ then "\nmin worker latency = " ++ show minLat
505+ then "\nmax worker latency = " ++ show maxLat
508+ then "\navg worker latency = "
509+ ++ show (avgTime `div` fromIntegral avgCnt)
472− else ""
510+ else "")
512+ then "\nSVar latency = " ++ show svarLat
475{-# NOINLINE dumpSVar #-}
516{-# NOINLINE dumpSVar #-}
@@ -498,8 +539,7 @@ dumpSVar sv = do
498 else return False
539 else return False
499 rthread <- readIORef $ workerThreads sv
540 rthread <- readIORef $ workerThreads sv
500 workers <- readIORef $ workerCount sv
541 workers <- readIORef $ workerCount sv
501− stats <- dumpSVarStats (svarStats sv) (svarStyle sv)
542+ stats <- dumpSVarStats sv (svarStats sv) (svarStyle sv)
502− (isJust (yieldRateInfo sv))
504 return $ unlines
544 return $ unlines
505 [ "tid = " ++ show tid
545 [ "tid = " ++ show tid
@@ -630,6 +670,9 @@ incrementYieldLimit sv =
630-- TBD Each worker can have their own queue and the consumer can empty one
670-- TBD Each worker can have their own queue and the consumer can empty one
631-- queue at a time, that way contention can be reduced.
671-- queue at a time, that way contention can be reduced.
673+-- XXX Only yields should be counted in the buffer limit and not the Stop
633-- | This function is used by the producer threads to queue output for the
676-- | This function is used by the producer threads to queue output for the
634-- consumer thread to consume. Returns whether the queue has more space.
677-- consumer thread to consume. Returns whether the queue has more space.
635send :: SVar t m a -> ChildEvent a -> IO Bool
678send :: SVar t m a -> ChildEvent a -> IO Bool
@@ -1126,7 +1169,7 @@ countDispatchWorkers workerLimit count duration wLatency expectedLat =
1126 -- XXX we can have a maxEfficiency combinator as well which runs the
1169 -- XXX we can have a maxEfficiency combinator as well which runs the
1127 -- producer at the maximal efficiency i.e. the number of workers are chosen
1170 -- producer at the maximal efficiency i.e. the number of workers are chosen
1128 -- such that the latency is minimum or within a range. Or we can call it
1171 -- such that the latency is minimum or within a range. Or we can call it
1129− -- maxLatency.
1172+ -- maxWorkerLatency.
1131 let nWorkers = (fromIntegral wLatency) / (fromIntegral expectedLat)
1174 let nWorkers = (fromIntegral wLatency) / (fromIntegral expectedLat)
1132 -- Calculate how many yields are we ahead or behind to match the exact
1175 -- Calculate how many yields are we ahead or behind to match the exact
@@ -1207,8 +1250,8 @@ useCollectedBatch yinfo latency colRef measRef = do
1207-- period. The former two are used for accurate measurement of the going rate
1250-- period. The former two are used for accurate measurement of the going rate
1208-- whereas the average is used for future estimates e.g. how many workers
1251-- whereas the average is used for future estimates e.g. how many workers
1209-- should be maintained to maintain the rate.
1252-- should be maintained to maintain the rate.
1210−collectLatency :: YieldRateInfo -> IO (Count, TimeSpec, NanoSecs)
1253+collectLatency :: SVarStats -> YieldRateInfo -> IO (Count, TimeSpec, NanoSecs)
1211−collectLatency yinfo = do
1254+collectLatency _ss yinfo = do
1212 let cur = workerCurrentLatency yinfo
1255 let cur = workerCurrentLatency yinfo
1213 col = workerCollectedLatency yinfo
1256 col = workerCollectedLatency yinfo
1214 longTerm = workerLongTermLatency yinfo
1257 longTerm = workerLongTermLatency yinfo
@@ -1228,6 +1271,14 @@ collectLatency yinfo = do
1228 if (pendingCount > 0)
1271 if (pendingCount > 0)
1230 let new = pendingTime `div` (fromIntegral pendingCount)
1273 let new = pendingTime `div` (fromIntegral pendingCount)
1275+ minLat <- readIORef (minWorkerLatency _ss)
1276+ when (new < minLat || minLat == 0) $
1277+ writeIORef (minWorkerLatency _ss) new
1279+ maxLat <- readIORef (maxWorkerLatency _ss)
1280+ when (new > maxLat) $ writeIORef (maxWorkerLatency _ss) new
1231 -- To avoid minor fluctuations update in batches
1282 -- To avoid minor fluctuations update in batches
1232 if (pendingCount > fromIntegral magicMaxBuffer)
1283 if (pendingCount > fromIntegral magicMaxBuffer)
1233 || (pendingTime > minThreadDelay)
1284 || (pendingTime > minThreadDelay)
@@ -1236,6 +1287,10 @@ collectLatency yinfo = do
1236 || (prev == 0)
1287 || (prev == 0)
1238 useCollectedBatch yinfo new col measured
1289 useCollectedBatch yinfo new col measured
1291+ modifyIORef (avgWorkerLatency _ss) $
1292+ \(cnt, t) -> (cnt + pendingCount, t + pendingTime)
1239 modifyIORef longTerm $ \(_, t) -> (lcount', t)
1294 modifyIORef longTerm $ \(_, t) -> (lcount', t)
1240 return (lcount', ltime, new)
1295 return (lcount', ltime, new)
@@ -1263,7 +1318,7 @@ dispatchWorkerPaced :: MonadAsync m => SVar t m a -> m Bool
1263dispatchWorkerPaced sv = do
1318dispatchWorkerPaced sv = do
1264 let workerLimit = maxWorkerLimit sv
1319 let workerLimit = maxWorkerLimit sv
1265 let yinfo = fromJust $ yieldRateInfo sv
1320 let yinfo = fromJust $ yieldRateInfo sv
1266− (count, tstamp, wLatency) <- liftIO $ collectLatency yinfo
1321+ (count, tstamp, wLatency) <- liftIO $ collectLatency (svarStats sv) yinfo
1267 -- XXX use workerBootstrapLatency
1322 -- XXX use workerBootstrapLatency
1268 if wLatency == 0
1323 if wLatency == 0
1269 -- Need to measure the latency with a single worker before we can perform
1324 -- Need to measure the latency with a single worker before we can perform
@@ -1547,13 +1602,15 @@ getAheadSVar st f = do
1547 Just x -> Just <$> newIORef x
1602 Just x -> Just <$> newIORef x
1548 rateInfo <- getYieldRateInfo st
1603 rateInfo <- getYieldRateInfo st
1551 disp <- newIORef 0
1605 disp <- newIORef 0
1552 maxWrk <- newIORef 0
1606 maxWrk <- newIORef 0
1553 maxOq <- newIORef 0
1607 maxOq <- newIORef 0
1554 maxHs <- newIORef 0
1608 maxHs <- newIORef 0
1555 maxWq <- newIORef 0
1609 maxWq <- newIORef 0
1556−#endif
1610+ avgLat <- newIORef (0, NanoSecs 0)
1611+ maxLat <- newIORef (NanoSecs 0)
1612+ minLat <- newIORef (NanoSecs 0)
1613+ stpTime <- newIORef Nothing
1558 let getSVar sv readOutput postProc = SVar
1615 let getSVar sv readOutput postProc = SVar
1559 { outputQueue = outQ
1616 { outputQueue = outQ
@@ -1577,14 +1634,18 @@ getAheadSVar st f = do
1577 , svarRef = Nothing
1634 , svarRef = Nothing
1578 , aheadWorkQueue = q
1635 , aheadWorkQueue = q
1579 , outputHeap = outH
1636 , outputHeap = outH
1580 , svarStats = SVarStats
1638 , svarStats = SVarStats
1581 { totalDispatches = disp
1639 { totalDispatches = disp
1582 , maxWorkers = maxWrk
1640 , maxWorkers = maxWrk
1583 , maxOutQSize = maxOq
1641 , maxOutQSize = maxOq
1584 , maxHeapSize = maxHs
1642 , maxHeapSize = maxHs
1585 , maxWorkQSize = maxWq
1643 , maxWorkQSize = maxWq
1644+ , avgWorkerLatency = avgLat
1645+ , minWorkerLatency = minLat
1646+ , maxWorkerLatency = maxLat
1647+ , svarStopTime = stpTime
1590 let sv =
1651 let sv =
@@ -1622,13 +1683,17 @@ getParallelSVar = do
1622 outQMv <- newEmptyMVar
1683 outQMv <- newEmptyMVar
1623 active <- newIORef 0
1684 active <- newIORef 0
1624 running <- newIORef S.empty
1685 running <- newIORef S.empty
1625−#ifdef DIAGNOSTICS
1686+
1626 disp <- newIORef 0
1687 disp <- newIORef 0
1627 maxWrk <- newIORef 0
1688 maxWrk <- newIORef 0
1628 maxOq <- newIORef 0
1689 maxOq <- newIORef 0
1629 maxHs <- newIORef 0
1690 maxHs <- newIORef 0
1630 maxWq <- newIORef 0
1691 maxWq <- newIORef 0
1631−#endif
1692+ avgLat <- newIORef (0, NanoSecs 0)
1693+ maxLat <- newIORef (NanoSecs 0)
1694+ minLat <- newIORef (NanoSecs 0)
1695+ stpTime <- newIORef Nothing
1632 let sv =
1697 let sv =
1633 SVar { outputQueue = outQ
1698 SVar { outputQueue = outQ
1634 , remainingYields = Nothing
1699 , remainingYields = Nothing
@@ -1651,14 +1716,18 @@ getParallelSVar = do
1651 , svarRef = Nothing
1716 , svarRef = Nothing
1652 , aheadWorkQueue = undefined
1717 , aheadWorkQueue = undefined
1653 , outputHeap = undefined
1718 , outputHeap = undefined
1654 , svarStats = SVarStats
1720 , svarStats = SVarStats
1655 { totalDispatches = disp
1721 { totalDispatches = disp
1656 , maxWorkers = maxWrk
1722 , maxWorkers = maxWrk
1657 , maxOutQSize = maxOq
1723 , maxOutQSize = maxOq
1658 , maxHeapSize = maxHs
1724 , maxHeapSize = maxHs
1659 , maxWorkQSize = maxWq
1725 , maxWorkQSize = maxWq
1726+ , avgWorkerLatency = avgLat
1727+ , minWorkerLatency = minLat
1728+ , maxWorkerLatency = maxLat
1729+ , svarStopTime = stpTime
1663 in return sv
1732 in return sv