Compute Speed of Download Algorithm
-
Currently, I am working on ZX2C4 Instant Messenger. ( http://zx2c4-im.sf.net ). For file transfers, a connection class raises an event for every 1024 bytes it receives (and finally length%1024 bytes at the end). This event is picked up by a form that has a progress bar. Everytime the event is called, I make the progress bar's value maximumvalue * (bytes trasfered/total). However, I lack a good way to compute the number of kb/s at which the file is downloading. Currently, when the progress bar is created, I assign a global varible called "start" to DateTime.Now, and every time the update event is signaled, I make the current speed equal to (bytes transfered) / (DateTime.Now - start). While this works, it only provides the average speed of the whole download. What is the best way to calculate the current speed of the download? Jason A. Donenfeld ZX2C4 Instant Messenger
zx2c4 wrote:
What is the best way to calculate the current speed of the download?
Since the event is conveniently raised after every 1KB is downloaded, I believe this is simply the 1/(ti - ti - 1) where "t" is the wall clock time. /ravi My new year's resolution: 2048 x 1536 Home | Music | Articles | Freeware | Trips ravib(at)ravib(dot)com
-
zx2c4 wrote:
What is the best way to calculate the current speed of the download?
Since the event is conveniently raised after every 1KB is downloaded, I believe this is simply the 1/(ti - ti - 1) where "t" is the wall clock time. /ravi My new year's resolution: 2048 x 1536 Home | Music | Articles | Freeware | Trips ravib(at)ravib(dot)com
-
Right. That's what I'm doing. But how do I make it not the average rate, but the present rate instead?
Oh, that's even easier! Just keep track of the total # bytes downloaded and the total elapsed time and divide the 2. :) [edit] Wait, my previous post gives you the present download rate. If you want the average download rate, see the current post. [/edit] /ravi My new year's resolution: 2048 x 1536 Home | Music | Articles | Freeware | Trips ravib(at)ravib(dot)com -- modified at 18:54 Wednesday 3rd May, 2006
-
Currently, I am working on ZX2C4 Instant Messenger. ( http://zx2c4-im.sf.net ). For file transfers, a connection class raises an event for every 1024 bytes it receives (and finally length%1024 bytes at the end). This event is picked up by a form that has a progress bar. Everytime the event is called, I make the progress bar's value maximumvalue * (bytes trasfered/total). However, I lack a good way to compute the number of kb/s at which the file is downloading. Currently, when the progress bar is created, I assign a global varible called "start" to DateTime.Now, and every time the update event is signaled, I make the current speed equal to (bytes transfered) / (DateTime.Now - start). While this works, it only provides the average speed of the whole download. What is the best way to calculate the current speed of the download? Jason A. Donenfeld ZX2C4 Instant Messenger
-
Oh, that's even easier! Just keep track of the total # bytes downloaded and the total elapsed time and divide the 2. :) [edit] Wait, my previous post gives you the present download rate. If you want the average download rate, see the current post. [/edit] /ravi My new year's resolution: 2048 x 1536 Home | Music | Articles | Freeware | Trips ravib(at)ravib(dot)com -- modified at 18:54 Wednesday 3rd May, 2006
Nono you miss read -- that does the average rate. I want the present rate. Doing 1/(difference between time now and time of last event) will produce sporatic behavior -- the number jumps around tons and tells nothign to the user. Is the only way to do this to just do 1*n / (difference between now and time of n events ago) ?
-
Keep the time for the last few blocks, and calculate the average speed for them. That way you get a more accurate speed than just calculating the speed of the last block. --- b { font-weight: normal; }
-
Nono you miss read -- that does the average rate. I want the present rate. Doing 1/(difference between time now and time of last event) will produce sporatic behavior -- the number jumps around tons and tells nothign to the user. Is the only way to do this to just do 1*n / (difference between now and time of n events ago) ?
zx2c4 wrote:
Doing 1/(difference between time now and time of last event) will produce sporatic behavior
Yes, it will if the download rate isn't constant. The average rate (average of all "present" rates) is nothing but the total # bytes downloaded divided by the time taken to download them. [edit] You could take Guffa's approach and calculate the average of the last "n" chunks. [/edit] /ravi My new year's resolution: 2048 x 1536 Home | Music | Articles | Freeware | Trips ravib(at)ravib(dot)com -- modified at 19:03 Wednesday 3rd May, 2006
-
zx2c4 wrote:
Doing 1/(difference between time now and time of last event) will produce sporatic behavior
Yes, it will if the download rate isn't constant. The average rate (average of all "present" rates) is nothing but the total # bytes downloaded divided by the time taken to download them. [edit] You could take Guffa's approach and calculate the average of the last "n" chunks. [/edit] /ravi My new year's resolution: 2048 x 1536 Home | Music | Articles | Freeware | Trips ravib(at)ravib(dot)com -- modified at 19:03 Wednesday 3rd May, 2006
-
well yes there is, but you said you didn't like it when you didn't group because the time frame was to0 small the the results were too erratic You only have three choices here: 1) display the total average transfer rate (which u already do). 2) display the rate at which the last 1k block came in (which you don't like) 3) group up 2, or 3, or 4 or 5 1k blocks and display a moving average Those are your choices...pick one. FWIW, you can do a fairly clean moving average by creating a variable for the total time it's taken for the last n number of blocks. Every time a new block comes in, figure out how much time went by, subtract the old average rate (sumx/n) then add the new amount of time and then display to the user the new sumx/n value. Note that the first time you subtract sumx will still be 0, so subtracting sumx -= sumx/n will still equal 0; This has the effect of the "download speed" starting out kind of slow then picking up until you reach n blocks, then it will be displaying the average of the last n blocks from there on out. You also don't really have to "group" anything or re-organize your downloads, just add one variable and do a little math. I've used that method for years to display moving averages and all kinds of other statistics (add a sumx^2 and a different equation to get standard deviations), about the only thing you have to watch out for is that your sumx doesn't overflow, but with todays 32bit integers and the numbers you're talking about that won't likely be an issue. It beats the heck out of having to allocate arrays or remember each individual download timeslice. Just one variable and a little math.
-
well yes there is, but you said you didn't like it when you didn't group because the time frame was to0 small the the results were too erratic You only have three choices here: 1) display the total average transfer rate (which u already do). 2) display the rate at which the last 1k block came in (which you don't like) 3) group up 2, or 3, or 4 or 5 1k blocks and display a moving average Those are your choices...pick one. FWIW, you can do a fairly clean moving average by creating a variable for the total time it's taken for the last n number of blocks. Every time a new block comes in, figure out how much time went by, subtract the old average rate (sumx/n) then add the new amount of time and then display to the user the new sumx/n value. Note that the first time you subtract sumx will still be 0, so subtracting sumx -= sumx/n will still equal 0; This has the effect of the "download speed" starting out kind of slow then picking up until you reach n blocks, then it will be displaying the average of the last n blocks from there on out. You also don't really have to "group" anything or re-organize your downloads, just add one variable and do a little math. I've used that method for years to display moving averages and all kinds of other statistics (add a sumx^2 and a different equation to get standard deviations), about the only thing you have to watch out for is that your sumx doesn't overflow, but with todays 32bit integers and the numbers you're talking about that won't likely be an issue. It beats the heck out of having to allocate arrays or remember each individual download timeslice. Just one variable and a little math.
-
This is exactly the sort of solution I'm looking for. However, I still do not understand completely the math involved. Can you write some psudo-code to help demonstrate this idea?
well now, I'm not going to do the whole thing for you. I don't know how you have it set up so it's a bit tough for me to guess. but assuming you have a function that gets called every time a 1k chunk is downloaded I'd do it this way: Forgive the lack of indenting, I never have figured out how to put tabs in these messages, I hit the tab key and my cursor jumps to the next block in the web page. I used spaces, but they don't make it either.
startdownload() { m_sumx = 0;//running sum of the moving average..initialize it m_numsamps = 3;//change to adjust sample size, otherwise just a fixed value } new1kblock()//this function gets called every time a 1k block arrives { int elapsedtime = whateveryouuse();//pretend it's in seconds, //I don't know what units you're dealing with, converting is up to you //m_sumx holds the sum of the last 3 values, first subtract 1/3 to keep // it from growing to infinity m_sumx -= m_sumx/m_numsamps; //then add the latest elapsed time m_sumx += elapsedtime; //ok, now sumx is again the sum of the last three values float dlspeed=0; if(m_sumx>0)//be careful to avoid divide by zero, it can happen dlspeed = 1024.0/(float)m_sumx/(float)m_numsamps; //in this example dlspeed is now a value representing how fast we // downloaded the last 3 1k chunks in bits/second. // Display it however you desire }
-
well now, I'm not going to do the whole thing for you. I don't know how you have it set up so it's a bit tough for me to guess. but assuming you have a function that gets called every time a 1k chunk is downloaded I'd do it this way: Forgive the lack of indenting, I never have figured out how to put tabs in these messages, I hit the tab key and my cursor jumps to the next block in the web page. I used spaces, but they don't make it either.
startdownload() { m_sumx = 0;//running sum of the moving average..initialize it m_numsamps = 3;//change to adjust sample size, otherwise just a fixed value } new1kblock()//this function gets called every time a 1k block arrives { int elapsedtime = whateveryouuse();//pretend it's in seconds, //I don't know what units you're dealing with, converting is up to you //m_sumx holds the sum of the last 3 values, first subtract 1/3 to keep // it from growing to infinity m_sumx -= m_sumx/m_numsamps; //then add the latest elapsed time m_sumx += elapsedtime; //ok, now sumx is again the sum of the last three values float dlspeed=0; if(m_sumx>0)//be careful to avoid divide by zero, it can happen dlspeed = 1024.0/(float)m_sumx/(float)m_numsamps; //in this example dlspeed is now a value representing how fast we // downloaded the last 3 1k chunks in bits/second. // Display it however you desire }
Thank you so much! The download speed seems much more sporatic though :'(. Here's my implementation:
public void UpdateStatus(double percentage) { this.progressBar1.Value = (int)Math.Round(((double)this.progressBar1.Maximum * percentage)); this.label2.Text = String.Format("Progress: {0}%", Math.Round(percentage * 100.0, 2)); _movingAverage -= _movingAverage / (double)SAMPLESIZE; _movingAverage += (DateTime.Now - _lastSegment).TotalSeconds; _lastSegment = DateTime.Now; if (_movingAverage > 0) { this.label1.Text = String.Format("Speed: {0} kb/s", Math.Round(((double)SAMPLESIZE * (double)StreamUtils.FilePacketSize / 1024.0) / _movingAverage, 2)); } else { this.label1.Text = "Speed: N/A"; } }
The download speed is strangly sporatic though... -
Thank you so much! The download speed seems much more sporatic though :'(. Here's my implementation:
public void UpdateStatus(double percentage) { this.progressBar1.Value = (int)Math.Round(((double)this.progressBar1.Maximum * percentage)); this.label2.Text = String.Format("Progress: {0}%", Math.Round(percentage * 100.0, 2)); _movingAverage -= _movingAverage / (double)SAMPLESIZE; _movingAverage += (DateTime.Now - _lastSegment).TotalSeconds; _lastSegment = DateTime.Now; if (_movingAverage > 0) { this.label1.Text = String.Format("Speed: {0} kb/s", Math.Round(((double)SAMPLESIZE * (double)StreamUtils.FilePacketSize / 1024.0) / _movingAverage, 2)); } else { this.label1.Text = "Speed: N/A"; } }
The download speed is strangly sporatic though...maybe things are happening faster than you think. Put a temporary label1.Text = String.Format("_movingAverage={0}", _movingAverage) statement to see what kind of values you are working with. ie. is it typically 0, 0, 3, 0, 3, 0, 3? or is it typically 35, 39, 31, 39...? What about adjust the SAMPLESIZE up, that have any effect? Tinker a little and use the debugger you'll get it :)