Calculating a running average

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);

Leave a Comment

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s