@@ -100,7 +100,6 @@ module Streamly.SVar
100100
101101 , toStreamVar
102102 , SVarStats (..)
103− , NanoSecs (..)
104103 , dumpSVar
105104 )
106105where
@@ -133,27 +132,15 @@ import Data.Set (Set)
133132import GHC.Conc (ThreadId(..))
134133import GHC.Exts
135134import GHC.IO (IO(..))
136−import System.Clock (TimeSpec, Clock(Monotonic), getTime, toNanoSecs)
135+import Streamly.Clock
136+ (AbsTime, NanoSecond64(..), MicroSecond64(..), getTime, diffAbsTime64,
137+ fromRelTime64, toRelTime64)
137138import System.IO (hPutStrLn, stderr)
138139import Text.Printf (printf)
139140
140141import qualified Data.Heap as H
141142import qualified Data.Set as S
142143
143−-- Always use signed arithmetic to avoid inadvertant overflows of signed values
144−-- on conversion when comparing unsigned quantities with signed.
145−newtype NanoSecs = NanoSecs Int64
146− deriving ( Eq
147− , Read
148− , Show
149− , Enum
150− , Bounded
151− , Num
152− , Real
153− , Integral
154− , Ord
155− )
156−
157144newtype Count = Count Int64
158145 deriving ( Eq
159146 , Read
@@ -230,7 +217,7 @@ data WorkerInfo = WorkerInfo
230217 -- total number of yields by the worker till now
231218 , workerYieldCount :: IORef Count
232219 -- yieldCount at start, timestamp
233− , workerLatencyStart :: IORef (Count, TimeSpec)
220+ , workerLatencyStart :: IORef (Count, AbsTime)
234221 }
235222
236223
@@ -262,13 +249,13 @@ data Rate = Rate
262249 }
263250
264251data LatencyRange = LatencyRange
265− { minLatency :: NanoSecs
266− , maxLatency :: NanoSecs
252+ { minLatency :: NanoSecond64
253+ , maxLatency :: NanoSecond64
267254 } deriving Show
268255
269256-- Rate control.
270257data YieldRateInfo = YieldRateInfo
271− { svarLatencyTarget :: NanoSecs
258+ { svarLatencyTarget :: NanoSecond64
272259 , svarLatencyRange :: LatencyRange
273260 , svarRateBuffer :: Int
274261 , svarGainedLostYields :: IORef Count
@@ -279,11 +266,11 @@ data YieldRateInfo = YieldRateInfo
279266 -- rate. The idle time of workers is adjusted in this, so that we only
280267 -- account for the rate when the consumer actually demands data.
281268 -- XXX interval latency is enough, we can move this under diagnostics build
282− , svarAllTimeLatency :: IORef (Count, TimeSpec)
269+ , svarAllTimeLatency :: IORef (Count, AbsTime)
283270
284271 -- XXX Worker latency specified by the user to be used before the first
285272 -- actual measurement arrives. Not yet implemented
286− , workerBootstrapLatency :: Maybe NanoSecs
273+ , workerBootstrapLatency :: Maybe NanoSecond64
287274
288275 -- After how many yields the worker should update the latency information.
289276 -- If the latency is high, this count is kept lower and vice-versa. XXX If
@@ -297,7 +284,7 @@ data YieldRateInfo = YieldRateInfo
297284 -- empty into workerCollectedLatency stats at certain intervals - whenever
298285 -- we process the stream elements yielded in this period.
299286 -- (yieldCount, timeTaken)
300− , workerPendingLatency :: IORef (Count, NanoSecs)
287+ , workerPendingLatency :: IORef (Count, NanoSecond64)
301288
302289 -- This is the second level stat which is an accmulation from
303290 -- workerPendingLatency stats. We keep accumulating latencies in this
@@ -305,10 +292,10 @@ data YieldRateInfo = YieldRateInfo
305292 -- to start collecting for the next period and retain the computed average
306293 -- latency for the last period in workerMeasuredLatency.
307294 -- (yieldCount, timeTaken)
308− , workerCollectedLatency :: IORef (Count, NanoSecs)
295+ , workerCollectedLatency :: IORef (Count, NanoSecond64)
309296
310297 -- Latency as measured by workers, aggregated for the last period.
311− , workerMeasuredLatency :: IORef NanoSecs
298+ , workerMeasuredLatency :: IORef NanoSecond64
312299 }
313300
314301data SVarStats = SVarStats {
@@ -317,10 +304,10 @@ data SVarStats = SVarStats {
317304 , maxOutQSize :: IORef Int
318305 , maxHeapSize :: IORef Int
319306 , maxWorkQSize :: IORef Int
320− , avgWorkerLatency :: IORef (Count, NanoSecs)
321− , minWorkerLatency :: IORef NanoSecs
322− , maxWorkerLatency :: IORef NanoSecs
323− , svarStopTime :: IORef (Maybe TimeSpec)
307+ , avgWorkerLatency :: IORef (Count, NanoSecond64)
308+ , minWorkerLatency :: IORef NanoSecond64
309+ , maxWorkerLatency :: IORef NanoSecond64
310+ , svarStopTime :: IORef (Maybe AbsTime)
324311}
325312
326313data Limit = Unlimited | Limited Word deriving Show
@@ -393,7 +380,7 @@ data State t m a = State
393380 , _threadsHigh :: Limit
394381 , _bufferHigh :: Limit
395382 -- XXX these two can be collapsed into a single type
396− , _streamLatency :: Maybe NanoSecs -- bootstrap latency
383+ , _streamLatency :: Maybe NanoSecond64 -- bootstrap latency
397384 , _maxStreamRate :: Maybe Rate
398385 , _inspectMode :: Bool
399386 }
@@ -505,7 +492,7 @@ setStreamLatency n st =
505492 else Just (fromIntegral n)
506493 }
507494
508−getStreamLatency :: State t m a -> Maybe NanoSecs
495+getStreamLatency :: State t m a -> Maybe NanoSecond64
509496getStreamLatency = _streamLatency
510497
511498setInspectMode :: State t m a -> State t m a
@@ -561,7 +548,7 @@ secs k
561548 | otherwise = printf "%.3f %s" t u
562549
563550-- XXX Code duplicated from collectLatency
564−drainLatency :: SVar t m a -> YieldRateInfo -> IO (Count, TimeSpec, NanoSecs)
551+drainLatency :: SVar t m a -> YieldRateInfo -> IO (Count, AbsTime, NanoSecond64)
565552drainLatency sv yinfo = do
566553 let cur = workerPendingLatency yinfo
567554 col = workerCollectedLatency yinfo
@@ -624,11 +611,11 @@ dumpSVarStats sv ss style = do
624611 gl <- readIORef (svarGainedLostYields yinfo)
625612 case t of
626613 Nothing -> do
627− now <- getTime Monotonic
628− let interval = toNanoSecs (now - startTime)
614+ now <- getTime
615+ let interval = diffAbsTime64 now startTime
629616 return (cnt, gl, interval `div` fromIntegral cnt)
630617 Just stopTime -> do
631− let interval = toNanoSecs (stopTime - startTime)
618+ let interval = diffAbsTime64 stopTime startTime
632619 return (cnt, gl, interval `div` fromIntegral cnt)
633620 else return (0, 0, 0)
634621
@@ -898,7 +885,7 @@ send sv msg = do
898885 active <- readIORef (workerCount sv)
899886 return $ len < (fromIntegral lim - active)
900887
901−workerCollectLatency :: WorkerInfo -> IO (Maybe (Count, NanoSecs))
888+workerCollectLatency :: WorkerInfo -> IO (Maybe (Count, NanoSecond64))
902889workerCollectLatency winfo = do
903890 (cnt0, t0) <- readIORef (workerLatencyStart winfo)
904891 cnt1 <- readIORef (workerYieldCount winfo)
@@ -906,8 +893,8 @@ workerCollectLatency winfo = do
906893
907894 if cnt > 0
908895 then do
909− t1 <- getTime Monotonic
910− let period = fromInteger $ toNanoSecs (t1 - t0)
896+ t1 <- getTime
897+ let period = fromRelTime64 $ diffAbsTime64 t1 t0
911898 writeIORef (workerLatencyStart winfo) (cnt1, t1)
912899 return $ Just (cnt, period)
913900 else return Nothing
@@ -1305,7 +1292,7 @@ pushWorker yieldMax sv = do
13051292 Nothing -> return Nothing
13061293 Just _ -> liftIO $ do
13071294 cntRef <- newIORef 0
1308− t <- getTime Monotonic
1295+ t <- getTime
13091296 lat <- newIORef (0, t)
13101297 return $ Just WorkerInfo
13111298 { workerYieldMax = yieldMax
@@ -1348,7 +1335,7 @@ pushWorkerPar sv wloop =
13481335 Nothing -> return Nothing
13491336 Just _ -> liftIO $ do
13501337 cntRef <- newIORef 0
1351− t <- getTime Monotonic
1338+ t <- getTime
13521339 lat <- newIORef (0, t)
13531340 return $ Just WorkerInfo
13541341 { workerYieldMax = 0
@@ -1420,24 +1407,21 @@ dispatchWorker yieldCount sv = do
14201407-- sleep time is accumulated.
14211408-- 3. Collected latencies are computed and transferred to measured latency
14221409-- after a minimum of this period.
1423−minThreadDelay :: NanoSecs
1424−minThreadDelay = 10^(6 :: Int)
1410+minThreadDelay :: NanoSecond64
1411+minThreadDelay = 1000000
14251412
14261413-- | Another magic number! When we have to start more workers to cover up a
14271414-- number of yields that we are lagging by then we cannot start one worker for
14281415-- each yield because that may be a very big number and if the latency of the
14291416-- workers is low these number of yields could be very high. We assume that we
14301417-- run each extra worker for at least this much time.
1431−rateRecoveryTime :: NanoSecs
1418+rateRecoveryTime :: NanoSecond64
14321419rateRecoveryTime = 1000000
14331420
1434−nanoToMicroSecs :: NanoSecs -> Int
1435−nanoToMicroSecs s = fromIntegral s `div` 1000
1436−
14371421-- We either block, or send one worker with limited yield count or one or more
14381422-- workers with unlimited yield count.
14391423data Work
1440− = BlockWait NanoSecs
1424+ = BlockWait NanoSecond64
14411425 | PartialWorker Count
14421426 | ManyWorkers Int Count
14431427 deriving Show
@@ -1447,9 +1431,9 @@ estimateWorkers
14471431 :: Limit
14481432 -> Count
14491433 -> Count
1450− -> NanoSecs
1451− -> NanoSecs
1452− -> NanoSecs
1434+ -> NanoSecond64
1435+ -> NanoSecond64
1436+ -> NanoSecond64
14531437 -> LatencyRange
14541438 -> Work
14551439estimateWorkers workerLimit svarYields gainLossYields
@@ -1500,7 +1484,7 @@ estimateWorkers workerLimit svarYields gainLossYields
15001484 fromIntegral rateRecoveryTime
15011485 yieldsFreq = 1.0 / fromIntegral targetLat
15021486 totalYieldsFreq = yieldsFreq + deltaYieldsFreq
1503− requiredLat = NanoSecs $ round $ 1.0 / totalYieldsFreq
1487+ requiredLat = NanoSecond64 $ round $ 1.0 / totalYieldsFreq
15041488 adjustedLat = min (max requiredLat (minLatency range))
15051489 (maxLatency range)
15061490 in assert (adjustedLat > 0) $
@@ -1527,7 +1511,7 @@ estimateWorkers workerLimit svarYields gainLossYields
15271511-- | Get the worker latency without resetting workerPendingLatency
15281512-- Returns (total yield count, base time, measured latency)
15291513-- CAUTION! keep it in sync with collectLatency
1530−getWorkerLatency :: YieldRateInfo -> IO (Count, TimeSpec, NanoSecs)
1514+getWorkerLatency :: YieldRateInfo -> IO (Count, AbsTime, NanoSecond64)
15311515getWorkerLatency yinfo = do
15321516 let cur = workerPendingLatency yinfo
15331517 col = workerCollectedLatency yinfo
@@ -1552,8 +1536,8 @@ getWorkerLatency yinfo = do
15521536isBeyondMaxRate :: SVar t m a -> YieldRateInfo -> IO Bool
15531537isBeyondMaxRate sv yinfo = do
15541538 (count, tstamp, wLatency) <- getWorkerLatency yinfo
1555− now <- getTime Monotonic
1556− let duration = fromInteger $ toNanoSecs $ now - tstamp
1539+ now <- getTime
1540+ let duration = fromRelTime64 $ diffAbsTime64 now tstamp
15571541 let targetLat = svarLatencyTarget yinfo
15581542 gainLoss <- readIORef (svarGainedLostYields yinfo)
15591543 let work = estimateWorkers (maxWorkerLimit sv) count gainLoss duration
@@ -1575,7 +1559,7 @@ isBeyondMaxRate sv yinfo = do
15751559-- value is updated an existing worker must check it again on the next yield.
15761560-- Otherwise it is possible that we may keep updating it and because of the mod
15771561-- worker keeps skipping it.
1578−updateWorkerPollingInterval :: YieldRateInfo -> NanoSecs -> IO ()
1562+updateWorkerPollingInterval :: YieldRateInfo -> NanoSecond64 -> IO ()
15791563updateWorkerPollingInterval yinfo latency = do
15801564 let periodRef = workerPollingInterval yinfo
15811565 cnt = max 1 $ minThreadDelay `div` latency
@@ -1589,7 +1573,7 @@ updateWorkerPollingInterval yinfo latency = do
15891573-- whereas the average is used for future estimates e.g. how many workers
15901574-- should be maintained to maintain the rate.
15911575-- CAUTION! keep it in sync with getWorkerLatency
1592−collectLatency :: SVar t m a -> YieldRateInfo -> IO (Count, TimeSpec, NanoSecs)
1576+collectLatency :: SVar t m a -> YieldRateInfo -> IO (Count, AbsTime, NanoSecond64)
15931577collectLatency sv yinfo = do
15941578 let cur = workerPendingLatency yinfo
15951579 col = workerCollectedLatency yinfo
@@ -1653,10 +1637,10 @@ dispatchWorkerPaced :: MonadAsync m => SVar t m a -> m Bool
16531637dispatchWorkerPaced sv = do
16541638 let yinfo = fromJust $ yieldRateInfo sv
16551639 (svarYields, svarElapsed, wLatency) <- do
1656− now <- liftIO $ getTime Monotonic
1640+ now <- liftIO $ getTime
16571641 (yieldCount, baseTime, lat) <-
16581642 liftIO $ collectLatency sv yinfo
1659− let elapsed = fromInteger $ toNanoSecs $ now - baseTime
1643+ let elapsed = fromRelTime64 $ diffAbsTime64 now baseTime
16601644 let latency =
16611645 if lat == 0
16621646 then
@@ -1698,7 +1682,8 @@ dispatchWorkerPaced sv = do
16981682 -- still have a Stop event waiting in the outputQueue.
16991683 done <- allThreadsDone sv
17001684 when done $ void $ do
1701− liftIO $ threadDelay $ nanoToMicroSecs s
1685+ let us = fromRelTime64 (toRelTime64 s) :: MicroSecond64
1686+ liftIO $ threadDelay (fromIntegral us)
17021687 dispatchWorker 1 sv
17031688 return False
17041689 PartialWorker yields -> do
@@ -1974,7 +1959,7 @@ getYieldRateInfo st = do
19741959 measured <- newIORef 0
19751960 wcur <- newIORef (0,0)
19761961 wcol <- newIORef (0,0)
1977− now <- getTime Monotonic
1962+ now <- getTime
19781963 wlong <- newIORef (0,now)
19791964 period <- newIORef 1
19801965 gainLoss <- newIORef (Count 0)
@@ -1999,9 +1984,9 @@ newSVarStats = do
19991984 maxOq <- newIORef 0
20001985 maxHs <- newIORef 0
20011986 maxWq <- newIORef 0
2002− avgLat <- newIORef (0, NanoSecs 0)
2003− maxLat <- newIORef (NanoSecs 0)
2004− minLat <- newIORef (NanoSecs 0)
1987+ avgLat <- newIORef (0, NanoSecond64 0)
1988+ maxLat <- newIORef (NanoSecond64 0)
1989+ minLat <- newIORef (NanoSecond64 0)
20051990 stpTime <- newIORef Nothing
20061991
20071992 return SVarStats