Calculating frames per second in a game

Viewed 122436

What's a good algorithm for calculating frames per second in a game? I want to show it as a number in the corner of the screen. If I just look at how long it took to render the last frame the number changes too fast.

Bonus points if your answer updates each frame and doesn't converge differently when the frame rate is increasing vs decreasing.

21 Answers

You need a smoothed average, the easiest way is to take the current answer (the time to draw the last frame) and combine it with the previous answer.

// eg.
float smoothing = 0.9; // larger=more smoothing
measurement = (measurement * smoothing) + (current * (1.0-smoothing))

By adjusting the 0.9 / 0.1 ratio you can change the 'time constant' - that is how quickly the number responds to changes. A larger fraction in favour of the old answer gives a slower smoother change, a large fraction in favour of the new answer gives a quicker changing value. Obviously the two factors must add to one!

This is what I have used in many games.

#define MAXSAMPLES 100
int tickindex=0;
int ticksum=0;
int ticklist[MAXSAMPLES];

/* need to zero out the ticklist array before starting */
/* average will ramp up until the buffer is full */
/* returns average ticks per frame over the MAXSAMPLES last frames */

double CalcAverageTick(int newtick)
{
    ticksum-=ticklist[tickindex];  /* subtract value falling off */
    ticksum+=newtick;              /* add new value */
    ticklist[tickindex]=newtick;   /* save new value so it can be subtracted later */
    if(++tickindex==MAXSAMPLES)    /* inc buffer index */
        tickindex=0;

    /* return average */
    return((double)ticksum/MAXSAMPLES);
}

Well, certainly

frames / sec = 1 / (sec / frame)

But, as you point out, there's a lot of variation in the time it takes to render a single frame, and from a UI perspective updating the fps value at the frame rate is not usable at all (unless the number is very stable).

What you want is probably a moving average or some sort of binning / resetting counter.

For example, you could maintain a queue data structure which held the rendering times for each of the last 30, 60, 100, or what-have-you frames (you could even design it so the limit was adjustable at run-time). To determine a decent fps approximation you can determine the average fps from all the rendering times in the queue:

fps = # of rendering times in queue / total rendering time

When you finish rendering a new frame you enqueue a new rendering time and dequeue an old rendering time. Alternately, you could dequeue only when the total of the rendering times exceeded some preset value (e.g. 1 sec). You can maintain the "last fps value" and a last updated timestamp so you can trigger when to update the fps figure, if you so desire. Though with a moving average if you have consistent formatting, printing the "instantaneous average" fps on each frame would probably be ok.

Another method would be to have a resetting counter. Maintain a precise (millisecond) timestamp, a frame counter, and an fps value. When you finish rendering a frame, increment the counter. When the counter hits a pre-set limit (e.g. 100 frames) or when the time since the timestamp has passed some pre-set value (e.g. 1 sec), calculate the fps:

fps = # frames / (current time - start time)

Then reset the counter to 0 and set the timestamp to the current time.

Increment a counter every time you render a screen and clear that counter for some time interval over which you want to measure the frame-rate.

Ie. Every 3 seconds, get counter/3 and then clear the counter.

Good answers here. Just how you implement it is dependent on what you need it for. I prefer the running average one myself "time = time * 0.9 + last_frame * 0.1" by the guy above.

however I personally like to weight my average more heavily towards newer data because in a game it is SPIKES that are the hardest to squash and thus of most interest to me. So I would use something more like a .7 \ .3 split will make a spike show up much faster (though it's effect will drop off-screen faster as well.. see below)

If your focus is on RENDERING time, then the .9.1 split works pretty nicely b/c it tend to be more smooth. THough for gameplay/AI/physics spikes are much more of a concern as THAT will usually what makes your game look choppy (which is often worse than a low frame rate assuming we're not dipping below 20 fps)

So, what I would do is also add something like this:

#define ONE_OVER_FPS (1.0f/60.0f)
static float g_SpikeGuardBreakpoint = 3.0f * ONE_OVER_FPS;
if(time > g_SpikeGuardBreakpoint)
    DoInternalBreakpoint()

(fill in 3.0f with whatever magnitude you find to be an unacceptable spike) This will let you find and thus solve FPS issues the end of the frame they happen.

You could keep a counter, increment it after each frame is rendered, then reset the counter when you are on a new second (storing the previous value as the last second's # of frames rendered)

Set counter to zero. Each time you draw a frame increment the counter. After each second print the counter. lather, rinse, repeat. If yo want extra credit, keep a running counter and divide by the total number of seconds for a running average.

In (c++ like) pseudocode these two are what I used in industrial image processing applications that had to process images from a set of externally triggered camera's. Variations in "frame rate" had a different source (slower or faster production on the belt) but the problem is the same. (I assume that you have a simple timer.peek() call that gives you something like the nr of msec (nsec?) since application start or the last call)

Solution 1: fast but not updated every frame

do while (1)
{
    ProcessImage(frame)
    if (frame.framenumber%poll_interval==0)
    {
        new_time=timer.peek()
        framerate=poll_interval/(new_time - last_time)
        last_time=new_time
    }
}

Solution 2: updated every frame, requires more memory and CPU

do while (1)
{
   ProcessImage(frame)
   new_time=timer.peek()
   delta=new_time - last_time
   last_time = new_time
   total_time += delta
   delta_history.push(delta)
   framerate= delta_history.length() / total_time
   while (delta_history.length() > avg_interval)
   {
      oldest_delta = delta_history.pop()
      total_time -= oldest_delta
   }
} 

In Typescript, I use this algorithm to calculate framerate and frametime averages:

let getTime = () => {
    return new Date().getTime();
} 

let frames: any[] = [];
let previousTime = getTime();
let framerate:number = 0;
let frametime:number = 0;

let updateStats = (samples:number=60) => {
    samples = Math.max(samples, 1) >> 0;

    if (frames.length === samples) {
        let currentTime: number = getTime() - previousTime;

        frametime = currentTime / samples;
        framerate = 1000 * samples / currentTime;

        previousTime = getTime();

        frames = [];
    }

    frames.push(1);
}

usage:

statsUpdate();

// Print
stats.innerHTML = Math.round(framerate) + ' FPS ' + frametime.toFixed(2) + ' ms';

Tip: If samples is 1, the result is real-time framerate and frametime.

This is based on KPexEA's answer and gives the Simple Moving Average. Tidied and converted to TypeScript for easy copy and paste:

Variable declaration:

fpsObject = {
  maxSamples: 100,
  tickIndex: 0,
  tickSum: 0,
  tickList: []
}

Function:

calculateFps(currentFps: number): number {
  this.fpsObject.tickSum -= this.fpsObject.tickList[this.fpsObject.tickIndex] || 0
  this.fpsObject.tickSum += currentFps
  this.fpsObject.tickList[this.fpsObject.tickIndex] = currentFps
  if (++this.fpsObject.tickIndex === this.fpsObject.maxSamples) this.fpsObject.tickIndex = 0
  const smoothedFps = this.fpsObject.tickSum / this.fpsObject.maxSamples
  return Math.floor(smoothedFps)
}

Usage (may vary in your app):

this.fps = this.calculateFps(this.ticker.FPS)

I adapted @KPexEA's answer to Go, moved the globals into struct fields, allowed the number of samples to be configurable, and used time.Duration instead of plain integers and floats.

type FrameTimeTracker struct {
    samples []time.Duration
    sum     time.Duration
    index   int
}

func NewFrameTimeTracker(n int) *FrameTimeTracker {
    return &FrameTimeTracker{
        samples: make([]time.Duration, n),
    }
}

func (t *FrameTimeTracker) AddFrameTime(frameTime time.Duration) (average time.Duration) {
    // algorithm adapted from https://stackoverflow.com/a/87732/814422
    t.sum -= t.samples[t.index]
    t.sum += frameTime
    t.samples[t.index] = frameTime
    t.index++
    if t.index == len(t.samples) {
        t.index = 0
    }
    return t.sum / time.Duration(len(t.samples))
}

The use of time.Duration, which has nanosecond precision, eliminates the need for floating-point arithmetic to compute the average frame time, but comes at the expense of needing twice as much memory for the same number of samples.

You'd use it like this:

// track the last 60 frame times
frameTimeTracker := NewFrameTimeTracker(60)

// main game loop
for frame := 0;; frame++ {
    // ...
    if frame > 0 {
        // prevFrameTime is the duration of the last frame
        avgFrameTime := frameTimeTracker.AddFrameTime(prevFrameTime)
        fps := 1.0 / avgFrameTime.Seconds()
    }
    // ...

}

Since the context of this question is game programming, I'll add some more notes about performance and optimization. The above approach is idiomatic Go but always involves two heap allocations: one for the struct itself and one for the array backing the slice of samples. If used as indicated above, these are long-lived allocations so they won't really tax the garbage collector. Profile before optimizing, as always.

However, if performance is a major concern, some changes can be made to eliminate the allocations and indirections:

  • Change samples from a slice of []time.Duration to an array of [N]time.Duration where N is fixed at compile time. This removes the flexibility of changing the number of samples at runtime, but in most cases that flexibility is unnecessary.
  • Then, eliminate the NewFrameTimeTracker constructor function entirely and use a var frameTimeTracker FrameTimeTracker declaration (at the package level or local to main) instead. Unlike C, Go will pre-zero all relevant memory.

Unfortunately, most of the answers here don't provide either accurate enough or sufficiently "slow responsive" FPS measurements. Here's how I do it in Rust using a measurement queue:

use std::collections::VecDeque;
use std::time::{Duration, Instant};

pub struct FpsCounter {
    sample_period: Duration,
    max_samples: usize,
    creation_time: Instant,
    frame_count: usize,
    measurements: VecDeque<FrameCountMeasurement>,
}

#[derive(Copy, Clone)]
struct FrameCountMeasurement {
    time: Instant,
    frame_count: usize,
}

impl FpsCounter {
    pub fn new(sample_period: Duration, samples: usize) -> Self {
        assert!(samples > 1);

        Self {
            sample_period,
            max_samples: samples,
            creation_time: Instant::now(),
            frame_count: 0,
            measurements: VecDeque::new(),
        }
    }

    pub fn fps(&self) -> f32 {
        match (self.measurements.front(), self.measurements.back()) {
            (Some(start), Some(end)) => {
                let period = (end.time - start.time).as_secs_f32();
                if period > 0.0 {
                    (end.frame_count - start.frame_count) as f32 / period
                } else {
                    0.0
                }
            }

            _ => 0.0,
        }
    }

    pub fn update(&mut self) {
        self.frame_count += 1;

        let current_measurement = self.measure();
        let last_measurement = self
            .measurements
            .back()
            .copied()
            .unwrap_or(FrameCountMeasurement {
                time: self.creation_time,
                frame_count: 0,
            });
        if (current_measurement.time - last_measurement.time) >= self.sample_period {
            self.measurements.push_back(current_measurement);
            while self.measurements.len() > self.max_samples {
                self.measurements.pop_front();
            }
        }
    }

    fn measure(&self) -> FrameCountMeasurement {
        FrameCountMeasurement {
            time: Instant::now(),
            frame_count: self.frame_count,
        }
    }
}

How to use:

  1. Create the counter: let mut fps_counter = FpsCounter::new(Duration::from_millis(100), 5);
  2. Call fps_counter.update() on every frame drawn.
  3. Call fps_counter.fps() whenever you like to display current FPS.

Now, the key is in parameters to FpsCounter::new() method: sample_period is how responsive fps() is to changes in framerate, and samples controls how quickly fps() ramps up or down to the actual framerate. So if you choose 10 ms and 100 samples, fps() would react almost instantly to any change in framerate - basically, FPS value on the screen would jitter like crazy, but since it's 100 samples, it would take 1 second to match the actual framerate.

So my choice of 100 ms and 5 samples means that displayed FPS counter doesn't make your eyes bleed by changing crazy fast, and it would match your actual framerate half a second after it changes, which is sensible enough for a game.

Since sample_period * samples is averaging time span, you don't want it to be too short if you want a reasonably accurate FPS counter.

store a start time and increment your framecounter once per loop? every few seconds you could just print framecount/(Now - starttime) and then reinitialize them.

edit: oops. double-ninja'ed

Related