In a newsgroup I replied to a question about calculating average data throughput during data reception over the network. A simple average bytes per second calculation needs only a few lines of code:
int bytesTotal = 0;
int bytesPerSecond = 0;
DateTime startTime = DateTime.Now;
do {
bytesRead = receiveStream.Read( ... );
bytesTotal += bytesRead;
bytesPerSecond = bytesTotal / (DateTime.Now - startTime).TotalSeconds;
...
} while(bytesRead > 0);
The problem with this is that it may not render very exact values over a longer download period because actual network throughput tends to change a lot over WAN connections. It would be more accurate to work out an average over a number of most recent measured values instead. As this requires some handling of a store of measured samples, I decided to create a reusable class that can hold a number of samples in a ring buffer and calculate the current average from these samples. The class takes a weighting of each sample into account, because in scenarios like the one above it’s not always guaranteed that new samples can be measured in an exact frequency. Here’s the class:
public class RunningAverage {
public RunningAverage(int samplesCount) {
samples = new Sample[samplesCount];
for (int i = 0; i < samplesCount; i++)
samples[i] = new Sample( );
currentSample = 0;
}
class Sample {
public Sample( ) { }
public Sample(double weighting, double measuredValue) {
Set(weighting, measuredValue);
}
public void Set(double weighting, double measuredValue) {
this.weighting = weighting;
this.measuredValue = measuredValue;
isValid = true;
}
private double weighting;
public double Weighting { get { return weighting; } }
private double measuredValue;
public double MeasuredValue { get { return measuredValue; } }
private bool isValid;
public bool IsValid { get { return isValid; } }
}
Sample[] samples;
int currentSample;
public void AddSample(double weighting, double measuredValue) {
samples[currentSample++].Set(weighting, measuredValue);
if (currentSample >= samples.Length)
currentSample = 0;
}
public double GetCurrentAverage( ) {
double totalValue = 0;
double totalWeighting = 0;
foreach (Sample sample in samples)
if (sample.IsValid) {
totalValue += sample.MeasuredValue;
totalWeighting += sample.Weighting;
}
return totalValue / totalWeighting;
}
}
Now the receive loop above could look like this:
RunningAverage average = new RunningAverage(100);
DateTime timeStamp = DateTime.Now;
do {
bytesRead = receiveStream.Read( ... );
DateTime newTimeStamp = DateTime.Now;
average.AddSample(newTimeStamp.TotalSeconds - timeStamp.TotalSeconds, bytesRead);
timeStamp = newTimeStamp;
// access average.GetCurrentAverage() if needed
...
} while(bytesRead > 0);