« DDD day 2 was a blastObject pooling, part 8 - monitoring the pool's performance »

Calculating a running average

23/10/05

Permalink 02:19:58 pm
Categories: General, Programming, .NET

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

No feedback yet

Leave a comment


Your email address will not be revealed on this site.
(Line breaks become <br />)
(For my next comment on this site)
(Allow users to contact me through a message form -- Your email will not be revealed!)
Please complete the song title below. Hint: enter 's', 'a', 't', 'i', 's', 'f', 'a', 'c', 't', 'i', 'o', 'n'
antispam test

Enter your email address:

Search

Oliver
MVP logo
May 2013
Sun Mon Tue Wed Thu Fri Sat
 << <   > >>
      1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31