on master help sign in
make custom rate combinators functions
482b971Harendra Kumar
benchmark/Linear.hs+2−2
@@ -130,7 +130,7 @@ main = do
130 (maxThreads 1 . Ops.sourceUnfoldrM)
130 (maxThreads 1 . Ops.sourceUnfoldrM)
131 -- XXX arbitrarily large maxRate should be the same as maxRate -1
131 -- XXX arbitrarily large maxRate should be the same as maxRate -1
132 , benchSrcIO asyncly "unfoldrM rate AvgRate 1000000"
132 , benchSrcIO asyncly "unfoldrM rate AvgRate 1000000"
133− (rate (Just $ MaxRate 1000000) . Ops.sourceUnfoldrM)
133+ (avgRate 1000000 . Ops.sourceUnfoldrM)
134 , benchSrcIO asyncly "unfoldrM maxBuffer 1 (1000 ops)"
134 , benchSrcIO asyncly "unfoldrM maxBuffer 1 (1000 ops)"
135 (maxBuffer 1 . Ops.sourceUnfoldrMN 1000)
135 (maxBuffer 1 . Ops.sourceUnfoldrMN 1000)
136 -- , benchSrcIO asyncly "fromFoldable" Ops.sourceFromFoldable
136 -- , benchSrcIO asyncly "fromFoldable" Ops.sourceFromFoldable
@@ -157,7 +157,7 @@ main = do
157 (maxThreads 1 . Ops.sourceUnfoldrM)
157 (maxThreads 1 . Ops.sourceUnfoldrM)
158 -- XXX arbitrarily large maxRate should be the same as maxRate -1
158 -- XXX arbitrarily large maxRate should be the same as maxRate -1
159 , benchSrcIO aheadly "unfoldrM rate AvgRate 1000000"
159 , benchSrcIO aheadly "unfoldrM rate AvgRate 1000000"
160− (rate (Just $ AvgRate 1000000) . Ops.sourceUnfoldrM)
160+ (avgRate 1000000 . Ops.sourceUnfoldrM)
161 , benchSrcIO aheadly "unfoldrM maxBuffer 1 (1000 ops)"
161 , benchSrcIO aheadly "unfoldrM maxBuffer 1 (1000 ops)"
162 (maxBuffer 1 . Ops.sourceUnfoldrMN 1000)
162 (maxBuffer 1 . Ops.sourceUnfoldrMN 1000)
163 -- , benchSrcIO aheadly "fromFoldable" Ops.sourceFromFoldable
163 -- , benchSrcIO aheadly "fromFoldable" Ops.sourceFromFoldable
examples/AcidRain.hs+1−2
@@ -22,8 +22,7 @@ userAction = S.repeatM $ liftIO askUser
22 _ -> putStrLn "What?" >> askUser
22 _ -> putStrLn "What?" >> askUser
23
23
24acidRain :: MonadAsync m => SerialT m Event
24acidRain :: MonadAsync m => SerialT m Event
25−acidRain = asyncly $ rate (Just $ ConstRate 1) $
26− S.repeatM $ liftIO $ return $ Harm 1
25+acidRain = asyncly $ constRate 1 $ S.repeatM $ liftIO $ return $ Harm 1
27
26
28game :: (MonadAsync m, MonadState Int m) => SerialT m ()
27game :: (MonadAsync m, MonadState Int m) => SerialT m ()
29game = do
28game = do
examples/CirclingSquare.hs+1−1
@@ -79,6 +79,6 @@ main :: IO ()
79main = do
79main = do
80 sdlInit
80 sdlInit
81 cref <- newIORef (0,0)
81 cref <- newIORef (0,0)
82− runStream $ asyncly $ rate (Just $ ConstRate 40)
82+ runStream $ asyncly $ constRate 40
83 $ S.repeatM (updateController cref)
83 $ S.repeatM (updateController cref)
84 `parallel` S.repeatM (updateDisplay cref)
84 `parallel` S.repeatM (updateDisplay cref)
src/Streamly.hs+7−1
@@ -112,8 +112,14 @@ module Streamly
112 -- $concurrency
112 -- $concurrency
113 , maxThreads
113 , maxThreads
114 , maxBuffer
114 , maxBuffer
115+
116+ -- * Rate Limiting
115 , Rate (..)
117 , Rate (..)
116 , rate
118 , rate
119+ , avgRate
120+ , minRate
121+ , maxRate
122+ , constRate
117
123
118 -- * Folding Containers of Streams
124 -- * Folding Containers of Streams
119 -- $foldutils
125 -- $foldutils
@@ -174,7 +180,7 @@ import Streamly.Streams.Ahead
174import Streamly.Streams.Parallel
180import Streamly.Streams.Parallel
175import Streamly.Streams.Zip
181import Streamly.Streams.Zip
176import Streamly.Streams.Prelude
182import Streamly.Streams.Prelude
177−import Streamly.Streams.SVar (maxThreads, maxBuffer, rate)
183+import Streamly.Streams.SVar
178import Streamly.SVar (MonadAsync, Rate (..))
184import Streamly.SVar (MonadAsync, Rate (..))
179import Data.Semigroup (Semigroup(..))
185import Data.Semigroup (Semigroup(..))
180
186
src/Streamly/SVar.hs+15−56
@@ -235,42 +235,17 @@ data WorkerInfo = WorkerInfo
235 , workerLatencyStart :: IORef (Count, TimeSpec)
235 , workerLatencyStart :: IORef (Count, TimeSpec)
236 }
236 }
237
237
238−-- | Specify the stream yield rate.
238+-- | Specify the stream yield rate in yields per second (@Hertz@). We try to
239+-- keep the average rate at 'rateGoal', if the actual average rate goes above
240+-- or below the goal we try to recover it by increasing or decreasing the
241+-- instantaneous rate but keeping it within 'rateLow' and 'rateHigh' limits.
239--
242--
240-- @since 0.5.0
243-- @since 0.5.0
241−data Rate =
244+data Rate = Rate
242− AvgRate Double
245+ { rateLow :: Double -- ^ The lower rate limit
243− -- ^ Specifies the average production rate of a stream in number of yields
246+ , rateGoal :: Double -- ^ The target rate we want to achieve
244− -- per second (i.e. @Hertz@). Concurrent production is ramped up or down
247+ , rateHigh :: Double -- ^ The upper rate limit
245− -- automatically to achieve the specified average yield rate. The rate can
248+ }
246− -- go down to half of the specified rate on the lower side and double of
247− -- the specified rate on the higher side.
248− | MinRate Double
249− -- ^ Specifies the minimum rate at which the stream should yield values. As
250− -- far as possible the yield rate would never be allowed to go below the
251− -- specified rate, even though it may possibly go above it at times, the
252− -- upper limit is double of the specified rate.
253− | MaxRate Double
254− -- ^ Specifies the maximum rate at which the stream should yield values. As
255− -- far as possible the yield rate would never be allowed to go above the
256− -- specified rate, even though it may possibly go below it at times, the
257− -- lower limit is half of the specified rate. This can be useful in
258− -- applications where certain resource usage must not be allowed to go
259− -- beyond certain limits.
260− | ConstRate Double
261− -- ^ Specifies a constant yield rate. If for some reason the actual rate
262− -- goes above or below the specified rate we do not try to recover it by
263− -- increasing or decreasing the rate in future. This can be useful in
264− -- applications like graphics frame refresh where we need to maintain a
265− -- constant refresh rate.
266− | Rate Double Double Double
267− -- ^ This is the most general specification of the stream yield rate and
268− -- all the other specifications can be expressed in terms of this. The
269− -- first argument is the target rate, the second argument is the lower
270− -- limit and the third argument is the upper limit. We try to keep the
271− -- average rate at the target rate specified, if the actual rate goes above
272− -- or below we try to recover the rate by increasing or decreasing it in
273− -- future while keeping the maximum or minimum within the specified range.
274
249
275data LatencyRange = LatencyRange
250data LatencyRange = LatencyRange
276 { minLatency :: NanoSecs
251 { minLatency :: NanoSecs
@@ -1828,29 +1803,13 @@ postProcessPaced sv = do
1828
1803
1829getYieldRateInfo :: State t m a -> IO (Maybe YieldRateInfo)
1804getYieldRateInfo :: State t m a -> IO (Maybe YieldRateInfo)
1830getYieldRateInfo st = do
1805getYieldRateInfo st = do
1831− let -- convert rate in Hertz to latency in Nanoseconds
1806+ -- convert rate in Hertz to latency in Nanoseconds
1832− rateToLatency r = if r <= 0 then maxBound else round $ 1.0e9 / r
1807+ let rateToLatency r = if r <= 0 then maxBound else round $ 1.0e9 / r
1833− -- handle overflowed latency value
1834− toMaxLatency l = if l <= 0 then maxBound else l
1835 case getStreamRate st of
1808 case getStreamRate st of
1836− Just (AvgRate rate) ->
1809+ Just (Rate low goal high) ->
1837− let l = rateToLatency rate
1838− mx = toMaxLatency (l * 2)
1839− in mkYieldRateInfo l (LatencyRange (l `div` 2) mx)
1840− Just (MinRate rate) ->
1841− let l = rateToLatency rate
1842− in mkYieldRateInfo l (LatencyRange (l `div` 2) l)
1843− Just (MaxRate rate) ->
1844− let l = rateToLatency rate
1845− mx = toMaxLatency (l * 2)
1846− in mkYieldRateInfo l (LatencyRange l mx)
1847− Just (ConstRate rate) ->
1848− let l = rateToLatency rate
1849− in mkYieldRateInfo l (LatencyRange l l)
1850− Just (Rate rate minr maxr) ->
1851− let l = rateToLatency rate
1810+ let l = rateToLatency goal
1852− minl = rateToLatency maxr
1811+ minl = rateToLatency high
1853− maxl = rateToLatency minr
1812+ maxl = rateToLatency low
1854 in mkYieldRateInfo l (LatencyRange minl maxl)
1813 in mkYieldRateInfo l (LatencyRange minl maxl)
1855 Nothing -> return Nothing
1814 Nothing -> return Nothing
1856
1815
src/Streamly/Streams/SVar.hs+55−3
@@ -33,6 +33,10 @@ module Streamly.Streams.SVar
33 , maxBuffer
33 , maxBuffer
34 , maxYields
34 , maxYields
35 , rate
35 , rate
36+ , avgRate
37+ , minRate
38+ , maxRate
39+ , constRate
36 )
40 )
37where
41where
38
42
@@ -203,11 +207,11 @@ maxBufferSerial _ = id
203rate :: IsStream t => Maybe Rate -> t m a -> t m a
207rate :: IsStream t => Maybe Rate -> t m a -> t m a
204rate r m = fromStream $ Stream $ \st stp sng yld -> do
208rate r m = fromStream $ Stream $ \st stp sng yld -> do
205 case r of
209 case r of
206− Just (Rate rt minr _) | rt < minr ->
210+ Just (Rate low goal _) | goal < low ->
207 error "rate: Target rate cannot be lower than minimum rate."
211 error "rate: Target rate cannot be lower than minimum rate."
208− Just (Rate rt _ maxr) | rt > maxr ->
212+ Just (Rate _ goal high) | goal > high ->
209 error "rate: Target rate cannot be greater than maximum rate."
213 error "rate: Target rate cannot be greater than maximum rate."
210− Just (Rate _ minr maxr) | minr > maxr ->
214+ Just (Rate low _ high) | low > high ->
211 error "rate: Minimum rate cannot be greater than maximum rate."
215 error "rate: Minimum rate cannot be greater than maximum rate."
212 _ -> unStream (toStream m) (setStreamRate r st) stp sng yld
216 _ -> unStream (toStream m) (setStreamRate r st) stp sng yld
213
217
@@ -217,6 +221,54 @@ yieldRateSerial :: Double -> SerialT m a -> SerialT m a
217yieldRateSerial _ = id
221yieldRateSerial _ = id
218-}
222-}
219
223
224+-- | Same as @rate (Just $ Rate (r/2) r (2*r))@
225+--
226+-- Specifies the average production rate of a stream in number of yields
227+-- per second (i.e. @Hertz@). Concurrent production is ramped up or down
228+-- automatically to achieve the specified average yield rate. The rate can
229+-- go down to half of the specified rate on the lower side and double of
230+-- the specified rate on the higher side.
231+--
232+-- @since 0.5.0
233+avgRate :: IsStream t => Double -> t m a -> t m a
234+avgRate r = rate (Just $ Rate (r/2) r (2*r))
235+
236+-- | Same as @rate (Just $ Rate r r (2*r))@
237+--
238+-- Specifies the minimum rate at which the stream should yield values. As
239+-- far as possible the yield rate would never be allowed to go below the
240+-- specified rate, even though it may possibly go above it at times, the
241+-- upper limit is double of the specified rate.
242+--
243+-- @since 0.5.0
244+minRate :: IsStream t => Double -> t m a -> t m a
245+minRate r = rate (Just $ Rate r r (2*r))
246+
247+-- | Same as @rate (Just $ Rate (r/2) r r)@
248+--
249+-- Specifies the maximum rate at which the stream should yield values. As
250+-- far as possible the yield rate would never be allowed to go above the
251+-- specified rate, even though it may possibly go below it at times, the
252+-- lower limit is half of the specified rate. This can be useful in
253+-- applications where certain resource usage must not be allowed to go
254+-- beyond certain limits.
255+--
256+-- @since 0.5.0
257+maxRate :: IsStream t => Double -> t m a -> t m a
258+maxRate r = rate (Just $ Rate (r/2) r r)
259+
260+-- | Same as @rate (Just $ Rate r r r)@
261+--
262+-- Specifies a constant yield rate. If for some reason the actual rate
263+-- goes above or below the specified rate we do not try to recover it by
264+-- increasing or decreasing the rate in future. This can be useful in
265+-- applications like graphics frame refresh where we need to maintain a
266+-- constant refresh rate.
267+--
268+-- @since 0.5.0
269+constRate :: IsStream t => Double -> t m a -> t m a
270+constRate r = rate (Just $ Rate r r r)
271+
220-- | Specify the average latency, in nanoseconds, of a single threaded action
272-- | Specify the average latency, in nanoseconds, of a single threaded action
221-- in a concurrent composition. Streamly can measure the latencies, but that is
273-- in a concurrent composition. Streamly can measure the latencies, but that is
222-- possible only after at least one task has completed. This combinator can be
274-- possible only after at least one task has completed. This combinator can be
test/MaxRate.hs+1−1
@@ -43,7 +43,7 @@ measureRate' desc t rval consumerDelay producerDelay dur = do
43 $ t
43 $ t
44 $ maxBuffer (-1)
44 $ maxBuffer (-1)
45 $ maxThreads (-1)
45 $ maxThreads (-1)
46− $ rate (Just $ AvgRate rval)
46+ $ avgRate rval
47 $ S.take (round $ rval * 10)
47 $ S.take (round $ rval * 10)
48 $ S.repeatM $ do
48 $ S.repeatM $ do
49 let (t1, t2) = producerDelay
49 let (t1, t2) = producerDelay
test/Prop.hs+5−5
@@ -693,7 +693,7 @@ main = hspec
693 let makeOps t =
693 let makeOps t =
694 [ ("default", t)
694 [ ("default", t)
695#ifndef COVERAGE_BUILD
695#ifndef COVERAGE_BUILD
696− , ("rate AvgRate 10000", t . rate (Just $ AvgRate 10000))
696+ , ("rate AvgRate 10000", t . avgRate 10000)
697 , ("rate Nothing", t . rate Nothing)
697 , ("rate Nothing", t . rate Nothing)
698 , ("maxBuffer 0", t . maxBuffer 0)
698 , ("maxBuffer 0", t . maxBuffer 0)
699 , ("maxBuffer 1", t . maxBuffer 1)
699 , ("maxBuffer 1", t . maxBuffer 1)
@@ -707,13 +707,13 @@ main = hspec
707 let serialOps :: IsStream t => ((SerialT IO a -> t IO a) -> Spec) -> Spec
707 let serialOps :: IsStream t => ((SerialT IO a -> t IO a) -> Spec) -> Spec
708 serialOps spec = mapOps spec $ (makeOps serially)
708 serialOps spec = mapOps spec $ (makeOps serially)
709#ifndef COVERAGE_BUILD
709#ifndef COVERAGE_BUILD
710− ++ [("rate AvgRate 0.00000001", serially . rate (Just $ AvgRate 0.00000001))]
710+ ++ [("rate AvgRate 0.00000001", serially . avgRate 0.00000001)]
711 ++ [("maxBuffer -1", serially . maxBuffer (-1))]
711 ++ [("maxBuffer -1", serially . maxBuffer (-1))]
712#endif
712#endif
713 let wSerialOps :: IsStream t => ((WSerialT IO a -> t IO a) -> Spec) -> Spec
713 let wSerialOps :: IsStream t => ((WSerialT IO a -> t IO a) -> Spec) -> Spec
714 wSerialOps spec = mapOps spec $ makeOps wSerially
714 wSerialOps spec = mapOps spec $ makeOps wSerially
715#ifndef COVERAGE_BUILD
715#ifndef COVERAGE_BUILD
716− ++ [("rate AvgRate 0.00000001", wSerially . rate (Just $ AvgRate 0.00000001))]
716+ ++ [("rate AvgRate 0.00000001", wSerially . avgRate 0.00000001)]
717 ++ [("maxBuffer (-1)", wSerially . maxBuffer (-1))]
717 ++ [("maxBuffer (-1)", wSerially . maxBuffer (-1))]
718#endif
718#endif
719 let asyncOps :: IsStream t => ((AsyncT IO a -> t IO a) -> Spec) -> Spec
719 let asyncOps :: IsStream t => ((AsyncT IO a -> t IO a) -> Spec) -> Spec
@@ -734,13 +734,13 @@ main = hspec
734 let parallelOps :: IsStream t => ((ParallelT IO a -> t IO a) -> Spec) -> Spec
734 let parallelOps :: IsStream t => ((ParallelT IO a -> t IO a) -> Spec) -> Spec
735 parallelOps spec = mapOps spec $ makeOps parallely
735 parallelOps spec = mapOps spec $ makeOps parallely
736#ifndef COVERAGE_BUILD
736#ifndef COVERAGE_BUILD
737− ++ [("rate AvgRate 0.00000001", parallely . rate (Just $ AvgRate 0.00000001))]
737+ ++ [("rate AvgRate 0.00000001", parallely . avgRate 0.00000001)]
738 ++ [("maxBuffer (-1)", parallely . maxBuffer (-1))]
738 ++ [("maxBuffer (-1)", parallely . maxBuffer (-1))]
739#endif
739#endif
740 let zipSerialOps :: IsStream t => ((ZipSerialM IO a -> t IO a) -> Spec) -> Spec
740 let zipSerialOps :: IsStream t => ((ZipSerialM IO a -> t IO a) -> Spec) -> Spec
741 zipSerialOps spec = mapOps spec $ makeOps zipSerially
741 zipSerialOps spec = mapOps spec $ makeOps zipSerially
742#ifndef COVERAGE_BUILD
742#ifndef COVERAGE_BUILD
743− ++ [("rate AvgRate 0.00000001", zipSerially . rate (Just $ AvgRate 0.00000001))]
743+ ++ [("rate AvgRate 0.00000001", zipSerially . avgRate 0.00000001)]
744 ++ [("maxBuffer (-1)", zipSerially . maxBuffer (-1))]
744 ++ [("maxBuffer (-1)", zipSerially . maxBuffer (-1))]
745#endif
745#endif
746 -- Note, the "pure" of applicative Zip streams generates and infinite
746 -- Note, the "pure" of applicative Zip streams generates and infinite