8

As a diagnostic, I want to display the number of cycles per second in my app. (Think frames-per-second in a first-person-shooter.)

But I don't want to display the most recent value, or the average since launch. What I want to calculate is the mean of the last X values.

My question is, I suppose, about the best way to store these values. My first thought was to create a fixed size array, so each new value would push out the oldest. Is this the best way to do it? If so, how would I implement it?

EDIT: Here's the class I wrote: RRQueue. It inherits Queue, but enforces the capacity and dequeues if necessary.

EDIT 2: Pastebin is so passé. Now on a GitHub repo.

Tom Wright
  • 11,278
  • 15
  • 74
  • 148

6 Answers6

16

The easiest option for this is probably to use a Queue<T>, as this provides the first-in, first-out behavior you're after. Just Enqueue() your items, and when you have more than X items, Dequeue() the extra item(s).

Reed Copsey
  • 554,122
  • 78
  • 1,158
  • 1,373
  • Would I have to copy to an array to get the mean of all the values? – Tom Wright Jun 22 '10 at 20:41
  • @Tom: No, the .NET generic queue implements `IEnumerable` so you can just enumerate over the elements to calculate your mean. – Ron Warholic Jun 22 '10 at 20:43
  • If you're using .NET 4.0 (possibly 3.5) you should be able to simply call the `.Sum()` and `.Count()` extension methods directly on the Queue. If you use this idiom a lot, it is trivial to create a `.Average()` extension method yourself. – drharris Jun 22 '10 at 20:43
  • @drharris: There is an Average<>() LINQ method for most types. Just call it directly: http://msdn.microsoft.com/en-us/library/system.linq.enumerable.average.aspx – Reed Copsey Jun 22 '10 at 20:52
  • 1
    @Tom: Just do: double average = myQueue.Average(); // If you're using Queue – Reed Copsey Jun 22 '10 at 20:53
  • My bad on that. For some reason Average doesn't show up in my intellisense but allows me to use it with no error. Now that's a new one. – drharris Jun 22 '10 at 20:56
  • @drharris: It isn't implemented for IEnumerable, but rather IEnumerable - since it needs a specific type to compute upon. That may be why you aren't seeing it... – Reed Copsey Jun 22 '10 at 21:58
14

A simple but fast implementation:

private int[] values = new int [10];  // all 0's initially
private int sum = 0;
private int pos = 0;

public void AddValue (int v)
{
   sum -= values[pos];  // only need the array to subtract old value
   sum += v;
   values[pos] = v;     
   pos = (pos + 1) % values.Length;    
}

public int Average => sum / values.Length;
H H
  • 263,252
  • 30
  • 330
  • 514
  • 1
    A small (subjective) improvement: `sum += v - values[pos]; values[pos++] = v; pos %= values.length;`. – heltonbiker May 09 '14 at 17:21
  • (also you could have a precomputed field `double divisor = 1.0 / values.length` and then `return sum * divisor` since division is more expensive than multiplication, but this is already quite paranoid, I admit...) – heltonbiker May 09 '14 at 17:24
  • 2
    _since division is more expensive than multiplication_ is only true for simple / older hardware. I would leave all these micro optimizations to the compiler(s). – H H May 10 '14 at 05:41
  • works great but in C# `Length` should be with a capital `L` – Couitchy Apr 11 '20 at 18:49
  • 1
    @Couitchy - fixed. That was a 10 year old typo )-: – H H Apr 12 '20 at 00:51
3

Possibly use a filter:

average = 0.9*average + 0.1*value where 'value' is the most recent measurement

Vary with the 0.9 and 0.1 (as long as the sum of these two is 1)

This is not exactly an average, but it does filter out spikes, transients, etc, but does not require arrays for storage.

Greetings, Karel

Karel
  • 31
  • 1
  • 1
    For applications where formal mathematical correctness is not needed, but rather a desired smoothing behavior, this idea is sure worth taking a look! – heltonbiker May 09 '14 at 17:26
1

If you need the fastest implementation, then yes, a fixed-size array ()with a separate count would be fastest.

Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810
0

You should take a look at the performance monitoring built into Windows :D.

MSDN

The API will feel a bit wonky if you haven't played with it before, but it's fast, powerful, extensible, and it makes quick work of getting usable results.

Aaron
  • 9
  • 2
0

my implementation:

class RoundRobinAverage
{
    int[] buffer;
    byte _size;
    byte _idx = 0;
    public RoundRobinAverage(byte size)
    {
        _size = size;
        buffer = new int[size];
    }

    public double Calc(int probeValue)
    {
        buffer[_idx++] = probeValue;
        if (_idx >= _size)
            _idx = 0;

        return buffer.Sum() / _size;
    }
}

usage:

private RoundRobinAverage avg = new RoundRobinAverage(10);\
...
var average = avg.Calc(123);
suszig
  • 310
  • 2
  • 4