Alternative to NSTimer

Nibbie
Posts: 2
Joined: 2009.05
Post: #61
This was of particular interest to me because I think this "main loop vs. nstimer" topic is the beating heart of your game engine. So, I tried to be as scientific as possible with my testing.

Device: iPhone 1st gen
OS: 3.1 beta
Build: default "Release" configuration

Project Baseline: Oolong Skeleton Project
The Oolong skeleton project simply rotates a multicolored cube in space. This should give us a nice "Apples to Apples" comparison of different event pumping mechanisms with unchanging rendering and FPS calculation code.

Changes I made for the testing
- removed VFP math (does not compile for >=3.0/gcc 4.2)
- enabled Multi-touch events on the EAGLView, wired up touch begin, end, moved, and cancelled events with NSLog("@touchEventName");
- Wired up different event types, you can see my code here:
code comparison gist
- If you're interested in playing along at home oolong is here, you'll need to patch it for your needs



Test phases:
a) FPS before touches
b) FPS while touching screen with up to 5 fingers for 20 seconds continuously
c) FPS 20 seconds after finishing test b

Test #1:
NSTimer (1/60)
Expectation: This is the default setting of the oolong skeleton project, except with multi-touch enabled, it will serve as our reference point
A: 57fps at boot, quickly ramps to 60fps
B: 45fps
C: 53fps
Result: I think we would all be happy to see 40-60fps in our game

Test #2
NSTimer(1/500)
Expectation: by boosting the number of possible calls, we should see a boost in framerate
A: 55fps at boot, quickly ramps to 60fps
B: 49fps
C: 51fps
Result: while true for the simulator where we jump above 330fps, this did not change much on the device.

Test #3
mainGameLoop(0.001)
Expectation: No more NSTimer, just a raw while loop. That's got to be better then firing all those objc messages, right?
A: 59, quickly ramps to 60
B: 41fps
C: 49fps
Result: Not what I expected, about the same frame rate, somewhat slower

Test #4
mainGameLoop(0.002)
Expectation: This should slow things down because we are waiting around longer for the system message calls, thus lowering our FPS, in return for better touch handling events. We can see a 100fps dip in the simulator.
A: 60fps
B: 53fps
C: 54fps
Result: Again, not what I expected, seems that the FPS actually got a boost. Maybe this is just simply closer to some internal constant, don't know why.

Test #5
mainGameLoop(0.004)
Expectation: This should slow things down, same logic as above
A: 60fps
B: 37fps
C: 48fps
Result: mostly correct. while doing the touch handling, the fps dipped more. However, no noticeable improvement in touch handling event precision was seen here.

Test #6
mainGameLoop(0.008)
Expectation: similar results to #5
A: 58, quickly ramps to 60fps
B: 40fps
C: 47fps
Result: mostly correct, except the framerate did not drop as much this time.

Conclusion:
It appears that the iPhone 1st gen device maxes out at about 60fps no matter what we do. The same exact code in the simulator on a MacBook Pro can easily render above 330fps using laptop hardware.

Switching from NSTimer to a mainGameLoop does not appear to improve anything, and may actually hinder performance. I cannot think of a scenario where I would need to specify the yield for system calls as it did not seem to effect the results of touch event handling. If there is such a scenario the only way to get that fined grained control at the sacrifice of drawing frame rate is to use the mainGameLoop mechanism.

I have to say, reading this thread had me sold on the mainGameLoop replacement mechanism before I did my testing. The results of this testing have convinced me that NSTimer is just fine in most cases. If my program is running at a slower framerate, the problem is probably somewhere else in my code, not inside of NSTimer.
Quote this message in a reply
Member
Posts: 283
Joined: 2006.05
Post: #62
That's very interesting; thanks for sharing it.
Quote this message in a reply
Member
Posts: 24
Joined: 2009.02
Post: #63
reklis Wrote:The Oolong skeleton project simply rotates a multicolored cube in space. This should give us a nice "Apples to Apples" comparison of different event pumping mechanisms with unchanging rendering and FPS calculation code.

The length of time taken to run through the render code is often a factor. I think rendering a simple cube (so, 12 triangles assuming you're not culling the back faces) is NOT a good enough representation of what your average game will be doing..

reklis Wrote:Switching from NSTimer to a mainGameLoop does not appear to improve anything, and may actually hinder performance.

I think you'll find if you're rendering a more complex scene, or have some physics simulations happening in your application, it might change things a bit. Especially with a low timer period (i.e. 1/60), where you'll start to notice beat frequencies when the timer misfires. Even though your average FPS might look ok, timer misfires cause stuttering or jerkiness in the animation. I've seen this on many applications on the app store.

Is your 'main loop' in a separate thread? (I think it would need to be). What priority did you set on the thread?

What are the times you are showing on your 'main thread' results (0.001, 0.002, etc.) Sleep time per cycle? They seem a bit arbitrary. I'm not sure what a random sleep period gains you. If you're trying to go as fast as possible, why sleep at all? The iPhone hardware refresh is 60Hz, so you *could* run a tight loop (no sleep) and allow the blocking in the OpenGL swap buffers calls to keep your drawing in check at 60fps.
Quote this message in a reply
Member
Posts: 446
Joined: 2002.09
Post: #64
reklis Wrote:It appears that the iPhone 1st gen device maxes out at about 60fps no matter what we do.
The screen is locked at a 60Hz refresh...

Still an interesting comparison. I keep going back and forth between an over-driven timer and a tight loop. I find timers yield to background tasks more erratically than tight loops, but regardless of how you drive frames just gotta be smart about updates. For the smoothest performance decouple your drawing from your physics/logic and interpolate the final frame.
Quote this message in a reply
Moderator
Posts: 133
Joined: 2008.05
Post: #65
No test is accurate on the simulator.

Furthermore, no test is going to be accurate when you are NSLogging back to the development machine. You're introducing another variable that you can't control and has significant impact on speed.
Quote this message in a reply
Member
Posts: 24
Joined: 2009.02
Post: #66
longjumper Wrote:Furthermore, no test is going to be accurate when you are NSLogging back to the development machine. You're introducing another variable that you can't control and has significant impact on speed.

Definitely! What I do is write the machine time (using mach_absolute_time()) on each draw (or better still, times at various stages during rendering) into an array in memory, then dump it out to the console at a later stage for analysis. This will add a little bit of time to the cycle, but it's fairly negligible. Logging out to the console on every frame is not negligible. Machine time can be converted to milliseconds with:

Code:
mach_timebase_info_data_t info;
mach_timebase_info(&info);
double machtime_conversion = (double)info.numer/(double)info.denom;
double timeInMs = machineTime * machtime_conversion * 1e-6
Quote this message in a reply
Member
Posts: 166
Joined: 2009.04
Post: #67
NSTimer tends to misfire and skip frames altogether ... ultimately nothing will be 100% smooth since this is not a real time OS we are dealing with, and you will have to interpolate one way or another ....but personally I found the loop approach to give me the most flexibility.

In any case, in the latest beta there is a new class specifically designed to handle this issue - it is called something like DisplayLink or such.
Quote this message in a reply
DoG
Moderator
Posts: 869
Joined: 2003.01
Post: #68
warmi Wrote:NSTimer tends to misfire and skip frames altogether ... ultimately nothing will be 100% smooth since this is not a real time OS we are dealing with, and you will have to interpolate one way or another ....but personally I found the loop approach to give me the most flexibility.

In any case, in the latest beta there is a new class specifically designed to handle this issue - it is called something like DisplayLink or such.

First of all, NSTimer doesn't misfire, so please stop spreading nonsense.

And second, you do realize that the 3.1 beta is under NDA?
Quote this message in a reply
Member
Posts: 166
Joined: 2009.04
Post: #69
DoG Wrote:First of all, NSTimer doesn't misfire, so please stop spreading nonsense.

And second, you do realize that the 3.1 beta is under NDA?

Ok ... it tends to skip frames, whatever you want to call it.

If you prefer NSTimer then that's your choice but its shortcomings are well known ( and thus the latest attempt to fix the problem by introducing a new class)

So Dog ... you can get agitated and call it nonsense all day long but 4-5 pages long threads on Apple's own forums worth of ideas how to fix the problem seem ( not to mention Apple's own attempts to fix it) suggest otherwise.
Quote this message in a reply
Member
Posts: 24
Joined: 2009.02
Post: #70
DoG Wrote:First of all, NSTimer doesn't misfire, so please stop spreading nonsense.

Yes, it does. If your code has not completed by the next time the timer is scheduled to fire, it misfires.

From Apples documentation for NSTimer:

Quote:A repeating timer reschedules itself based on the scheduled firing time, not the actual firing time. For example, if a timer is scheduled to fire at a particular time and every 5 seconds after that, the scheduled firing time will always fall on the original 5 second time intervals, even if the actual firing time gets delayed. If the firing time is delayed so far that it passes one or more of the scheduled firing times, the timer is fired only once for that time period; the timer is then rescheduled, after firing, for the next scheduled firing time in the future.

In other words, a timer can misfire. A misfire is simply skipping a fire.

I've seen this happen a lot on the iPhone, especially when doing some rapid prototyping. Before optimising the drawing code, if it takes close to 16.6 ms (which is the period for 60fps), the timer regularly fires with 33.3 ms intervals (double 16ms, as one fire has been skipped).

This is why mainly people oversample (trigger the timer at a much higher rate than required for 60fps), perhaps without realising it.
Quote this message in a reply
Moderator
Posts: 3,577
Joined: 2003.06
Post: #71
mpatric Wrote:Yes, it does. If your code has not completed by the next time the timer is scheduled to fire, it misfires.

[chuckle] That's not the timer misfiring, that's your code being too slow Rasp
Quote this message in a reply
Member
Posts: 166
Joined: 2009.04
Post: #72
AnotherJake Wrote:[chuckle] That's not the timer misfiring, that's your code being too slow Rasp

Yeah, that's one way of putting it ...

On the other hand going 2 ms over whatever is your time limit per frame should not cause fps to drop by 50%, should it ?
Quote this message in a reply
Moderator
Posts: 133
Joined: 2008.05
Post: #73
I really wish an admin would just delete this thread. There is so much bullshit and ignorance in here, it's just going to confuse people actually looking for help.
Quote this message in a reply
Member
Posts: 166
Joined: 2009.04
Post: #74
longjumper Wrote:I really wish an admin would just delete this thread. There is so much bullshit and ignorance in here, it's just going to confuse people actually looking for help.

The only posts worth deleting are the ones talking about bullshit and ignorance without contributing anything.
Quote this message in a reply
Moderator
Posts: 3,577
Joined: 2003.06
Post: #75
warmi Wrote:Yeah, that's one way of putting it ...

On the other hand going 2 ms over whatever is your time limit per frame should not cause fps to drop by 50%, should it ?
You are definitely doing something terribly wrong if that's what's happening to you with a timer.

For the record: I've tried all kinds of different approaches to this on the iPhone, with and without a timer. The results were pretty much the same: good enough. Using a fixed rate time-based update I get plenty good performance any way I've tried it, so I stick with the timer for simplicity. Even with a moderately busy scene I can get a smooth 45 fps, and pushing thousands of lit and depth tested triangles it'll drop down to 20 fps, but still remain smooth enough to play, even with touch input, so I have no earthly idea what all the complaining is about.

Here's how it is: There are those of us that are doing just fine with timers and have seen no justifiably better performance out of other techniques, and then there are those of you who seem to be swearing up and down that we're lying. Ten thousand pages of complaints about timers on Apple's forums doesn't mean much, other than many folks can't seem to figure out what's going on with their own code and would prefer to blame their own incompetence on Apple.

longjumpe Wrote:I really wish an admin would just delete this thread. There is so much bullshit and ignorance in here, it's just going to confuse people actually looking for help.
Believe me, the thought has crossed my mind more than once... Sneaky
Quote this message in a reply
Post Reply 

Possibly Related Threads...
Thread: Author Replies: Views: Last Post
  NSTimer fine, CADisplayLink having some issues monteboyd 5 10,039 Aug 31, 2010 07:05 PM
Last Post: Skorche
  NSTimer hiccups/choppy/jerky jeonghyunhan 2 3,005 Sep 24, 2009 07:35 PM
Last Post: jeonghyunhan
  Using an NSTimer to progressively draw a view StevenD 4 4,607 May 14, 2009 07:57 AM
Last Post: StevenD
  OpenGL render loop - NSTimer vs rendering thread smallstepforman 27 24,072 Feb 2, 2009 10:22 AM
Last Post: ThemsAllTook
  Alternative Input/Control Ideas chrisco 8 4,552 Aug 6, 2008 03:21 PM
Last Post: bruss14