2

I'm trying to get the current download speed of a WebClient downloading a file, however when I use a formula that I'm pretty sure should work out:

Stopwatch.Stop();
double msElapsed = Stopwatch.Elapsed.TotalMilliseconds;
int bytesDownloaded = (int)e.BytesReceived - lastBytesDownloaded;
double downloadSpeed = (double)bytesDownloaded / (msElapsed / 1000);
lastBytesDownloaded = (int)e.BytesReceived;
Stopwatch.Restart();

Where Stopwatch is a stopwatch that I've started just as I started the file download, lastBytesDownloaded is a class variable, and this is all inside the downloadProgressChanged event, however the download speed varies wildly off course from what it actually is.

For example if I was downloading a file at 500kb/s, it would rapidly jump from (for example) 10kb/s to 50mb/s completely randomly.

I can get an accurate average download time by making a couple edits to that:

double sElapsed = Stopwatch.Elapsed.TotalSeconds;
int bytesDownloaded = (int)e.BytesReceived;
double downloadSpeed = bytesDownloaded / sElapsed;

But that isn't what I want. How can I get a more stable reading for current download speed?

  • You want to do a moving average, Rx.NET is a really nice way to do this. PS Thanks for this example, which I will have to steal for an RxJS talk next month :P [Check out this example.](http://stackoverflow.com/questions/14805392/rx-stateful-transform-of-sequence-e-g-exponential-moving-average) – Aron Oct 06 '13 at 19:25

1 Answers1

2

You just need to smooth the data over a longer period of time. For example, don't report current download speed only on the basis of the last measurement; use a (perhaps weighted) moving average instead.

Dead simple example:

var measurements = 0, maxDataPoints = 5;
var dataPoints = new double[maxDataPoints];

And then:

Stopwatch.Stop();
double msElapsed = Stopwatch.Elapsed.TotalMilliseconds;
int bytesDownloaded = (int)e.BytesReceived - lastBytesDownloaded;
lastBytesDownloaded = (int)e.BytesReceived;
double dataPoint = (double)bytesDownloaded / (msElapsed / 1000);
dataPoints[measurements++ % maxDataPoints] = dataPoint;

double downloadSpeed = dataPoints.Average();
Stopwatch.Restart();
Jon
  • 428,835
  • 81
  • 738
  • 806
  • This is wonderful, along with another couple tweaks I've made (sometimes the watch would report < 1ms, I just used a Math.Max to set a lower bound) this worked quite nicely. Is there any max data points or other things you can suggest to make it more consistent? –  Oct 07 '13 at 01:07