Not a very good title, but I didn't know what to name it.
Anyway, I am counting the total frames (so I can calculate an average FPS) in my game with a long int
. Just in case the game went on reallly long, what should I do to make sure my 开发者_StackOverflowlong int
doesn't get incremented past its limit? And what would happen if it did go past its limit?
Thanks.
This problem is present for any kind of counters.
For your specific problem, I wouldn't worry.
A long int
counts up to 2 billions (and more) in most worst cases (on 32 bit computers/consoles). Supposing your game is doing 1000 frames per second (which is a lot!), it would take 20000000 seconds to overflow your counter: more than 5000 hours, more than 231 days.
I'm pretty sure something else would cause your game to stop, if you try to run it for that long!
I would instead consider using an exponentially-weighted moving average. That approach will kill two birds with one stone: it will avoid the problem of accumulating a large number, and it will also adapt to recent behavior so that an accumulated average of 100fps in the year 2010 would not skew the average so that a 2fps rate would seem acceptable for a month or so in 2011 :).
Average FPS throughout the length of an entire game doesn't seem to be a very useful statistic. Typically you will wish to measure peaks and valleys, such as highest fps / lowest fps and amount of frames spent below and above threshold values.
In reality though, I would not worry. Even if you were to just use a 32 bit unsigned int, your game could run at 60fps for 19884 hours before it would overflow. You should be fine.
EDIT:
The best way to detect overflow in this case is to check and see if the integer decreased in value after being incremented. If so, you could just keep another counter around which is the number of times you have overflowed.
You could actively check for an overflow in your arithmetic operations. E. g. SafeInt can do that for you. Of course, the performance is worse than for i++
.
However, it is unlikely that a 32 bit integer will overflow if you always increment by one.
If long int is 32-bits, the maximum value is 2^31-1, so with 1ms updates it will overflow in 24.9 days, not 231 [2^31/1000/60/60/24].
Hopefully not too OT... generally for games this may not really be an issue, but it is for other applications. A common mistake to be careful of is doing something like
extern volatile uint32_t counter;
uint32_t one_second_elapsed = counter + 1000;
while ( counter < one_second_elapsed ) do_something();
If counter + 1000 overflows, then do_something() will not be called. The way to check this is,
uint32_t start = counter;
while ( counter - start < 1000 ) do_something();
It's probably better to use an average over a small number of frames. You mentioned that you want to calculate an average, but there is really no reason to keep such a larger number of samples around to calculate an average. Just keep a running total of frametimes over some small period of time (where small could be something between 10-50 frames - we typically use 16). You can then use that total to calculate an average frames per second. This method also helps smooth out frame time reports so that the numbers don't jump all over the place. One thing to watch out for though is that if you average over too long of a time period, frame rate spikes become more "hidden", meaning it might be tougher to spot frames which cause framerate to drop if those frames only happen every so often.
Something like this would be totally sufficient I think (non-tested code to follow):
// setup some variables once
const int Max_samples = 16; // keep at most 16 frametime samples
int FPS_Samples = 0;
int Current_sample = 0;
int Total_frametime = 0.0f;
float Frametimes[Max_samples];
for ( int i = 0; i < Max_samples; i++ ) {
Frametimes[i] = 0.0f;
Then when you calculate your frametime, you could do something like this:
// current_frametime is the new frame time for this frame
Total_frametime -= Frametimes[Current_sample];
Total_frametime += current_frametime;
Frametimes[Current_sample] = current_frametime;
Current_sample = ( Current_sample + 1 ) % Max_samples; // move to next element in array
Frames_per_second = Max_samples / Total_frametime;
It's a rough cut and could probably use some error checking, but it gives the general idea.
精彩评论