Better way to code download speed calcuation

Posted on

Problem

I’m writing a program that downloads stuff from the internet. While the program is working fine, I want to get one critical part reviewed. I use System.Diagnostics.StopWatch object to measure the time taken for the ns (NetworkStream) object to read the bytes in buffer. I then divide the number of elapsed seconds in stopwatch by number of bytes received to calculate the speed of my download in KBPS.

However, this method is giving me some extraordinarily large speeds (in excess of 8000KBPS), whereas I know that my internet bandwidth is not that fast. Is there anything wrong with this method? If not, then what is the correct way of getting the download speed? Here is my code:

while(true) //(bytesToRead>0)
{

    sw.Start();
    int n = ns.Read(buffer, 0, buffer.Length);
    sw.Stop();
    Speed = (float)n / sw.Elapsed.TotalSeconds;

    if (n==0)
        break;

    fs.Write(buffer,0,n);

    BytesRead+=n; //TODO: Persist the bytesRead object somewhere, so that it can be taken back for resumes
    bytesToRead -= n;
    OnProgress(this,new System.EventArgs());
    if (Status == DownloadStatusEnum.Paused) break;
}

Solution

Your summary complaint is that you’re measuring short term xfer rate from system buffers to user space, rather than long term xfer rate from distant server to local network interface.

After a few roundtrips, TCP opens the congestion window and can have many KiB of data in flight. I don’t know what your buffer.length is, but you make it sound like it is short enough that you’re not blocking on read. That is, ns.Read() finds quite a lot of data buffered at the OS level, and returns a buffer’s worth of it at once, so that your timing represents local CPU / memory bandwidth rather than network bandwidth.

The simplest remedy is to record initial starting time and don’t touch it, report cumulative bytes read over cumulative elapsed time.

If your residential ISP does traffic shaping, and you want the display to reflect the fact that the first 30 seconds goes at 3 Mbps but longer downloads go at 1 Mbps, then keep a circular buffer of the last dozen or so timestamps, and use that to report on recent progress. Old measurements will age out, so after a while they will no longer affect the display.

I then divide the number of elapsed seconds in stopwatch by number of
bytes received to calculate the speed of my download in KBPS.

Just an idea: NetworkStream.Read returns the number of bytes received. If you divide this with the number of seconds, you’ll get the download speed in bytes per second.

Leave a Reply

Your email address will not be published. Required fields are marked *