improved bwrate limiting implementation

New method is much better. Avoids unrestrained transfer at the beginning
(except for the first block. Keeps right at or a few kb/s below the
configured limit, with very little varation in the actual reported bandwidth.

Removed the /s part of the config as it's not needed.

Ready to merge.

Sponsored-by: Luke Shumaker on Patreon
This commit is contained in:
Joey Hess 2021-09-22 15:14:28 -04:00
parent 44d3d50785
commit e8496d62e4
No known key found for this signature in database
GPG key ID: DB12DB0FF05F8F38
5 changed files with 46 additions and 55 deletions

View file

@ -389,39 +389,34 @@ rateLimitMeterUpdate delta (Meter totalsizev _ _ _) meterupdate = do
-- same process and thread as the call to the MeterUpdate.
--
-- For example, if the desired bandwidth is 100kb/s, and over the past
-- second, 200kb was sent, then pausing for half a second, and then
-- running for half a second should result in the desired bandwidth.
-- But, if after that pause, only 75kb is sent over the next half a
-- second, then the next pause should be 2/3rds of a second.
-- 1/10th of a second, 30kb was sent, then the current bandwidth is
-- 300kb/s, 3x as fast as desired. So, after getting the next chunk,
-- pause for twice as long as it took to get it.
bwLimitMeterUpdate :: ByteSize -> Duration -> MeterUpdate -> IO MeterUpdate
bwLimitMeterUpdate sz duration meterupdate = do
nowtime <- getPOSIXTime
lastpause <- newMVar (nowtime, toEnum 0 :: POSIXTime, 0)
return $ mu lastpause
where
mu lastpause n@(BytesProcessed i) = do
bwLimitMeterUpdate bwlimit duration meterupdate
| bwlimit <= 0 = return meterupdate
| otherwise = do
nowtime <- getPOSIXTime
meterupdate n
lastv@(prevtime, prevpauselength, previ) <- takeMVar lastpause
let timedelta = nowtime - prevtime
if timedelta >= durationsecs
then do
let sz' = i - previ
let runtime = timedelta - prevpauselength
let pauselength = calcpauselength sz' runtime
if pauselength > 0
then do
unboundDelay (floor (pauselength * fromIntegral oneSecond))
putMVar lastpause (nowtime, pauselength, i)
else putMVar lastpause lastv
else putMVar lastpause lastv
mv <- newMVar (nowtime, 0)
return (mu mv)
where
mu mv n@(BytesProcessed i) = do
endtime <- getPOSIXTime
(starttime, previ) <- takeMVar mv
calcpauselength sz' runtime
| sz' > sz && sz' > 0 && runtime > 0 =
durationsecs - (fromIntegral sz / fromIntegral sz') * runtime
| otherwise = 0
durationsecs = fromIntegral (durationSeconds duration)
let runtime = endtime - starttime
let currbw = fromIntegral (i - previ) / runtime
let pausescale = if currbw > bwlimit'
then (currbw / bwlimit') - 1
else 0
unboundDelay (floor (runtime * pausescale * msecs))
meterupdate n
nowtime <- getPOSIXTime
putMVar mv (nowtime, i)
bwlimit' = fromIntegral (bwlimit * durationSeconds duration)
msecs = fromIntegral oneSecond
data Meter = Meter (MVar (Maybe TotalSize)) (MVar MeterState) (MVar String) DisplayMeter