Should timers in an engine be on separate threads?

Started by
13 comments, last by ROGRat 5 years, 6 months ago

I'm making an small 2D engine using Kha and I have a timer class, which basically simply either waits a certain amount of time to call a function, or repeatedly calls a certain function after every x seconds. I simply want to know if I should have timers run on different threads. I'm aware that makes sense, but I might use many timers in a game for example, would that still be okay? Also I'm currently writing an animation components, which waits every x seconds to draw another image using the timer class. And in a normal 2D games, I would have many objects with animations on them, other than the other timers. So I just wanted to ask people who have more experience and knowledge than I have what I should do for timers: Either leave them on the same main thread, or make them run on different threads. Thanks in advance.

Advertisement

The main question is what else runs in a different thread of your game? It dosent make sense to split a sepratate thread for timers if you have 10 other threads running whatever logic in the background.

Animation is usually done using the time delta from last frame to current frame. I don't know any kind of engine that uses a separate timer class for anymations. Time delta is calculated by getting the time on the start of last frame and the time of the start of current frame, this way you know how much time expired between your two frames and can mult that factor to anything, any animation, any movement and whatever you also have.

In my engine, I use a pooling for tasks that may run in any thread my scheduler gives it so I start a timer in one thread and check for it in another one for example on update loop or when reaching a code part that utilizes the timer. This is the reason why timers and timing functions write in there docs to wayt "at least a minimum ammount of time before executing callback". This is because you won't have exact timing even if you run a timer thread because threads may schedule from the OS and if you have a large list of timers, it becomes an O(n) operation until your callback returns.

I use high precision timers, so my "Timer-Class" is just an uint64 value with the CPU tick count. To get the time elapsed I then throw that into a measure function that calculates the difference between my 'timer' and current CPU ticks, thats it.

Timers are in my experience barely used except for gameplay features, anything else that is frequently updated, like ingame-time for example, is usually bound to the time delta of the frame

It's probably overkill unless the resulting function call is also on another thread, and even then it might be overkill. You're not going to gain much putting a timer on another thread, except possibly the headache of debugging resulting threading issues. ;) 

If you find you need to, there's nothing stopping you from just spawning the timer on the separate thread as needed - there's no need to incorporate threading behavior in the timer itself. 

On 6/9/2018 at 9:49 PM, Liquifire said:

I might use many timers in a game for example, would that still be okay?

For being "okay", it basically boils down to costs and purposes.

Threads have a cost. There is a relatively high cost to create a thread, and threads have some processing overhead. The functionality is written and provided by the operating system rather than code you write in your application, but it is still code that gets run, it still has a cost to run, and the work of scheduling, context switching, and otherwise manipulating the processing threads still needs to happen.

If your game is still running anyway, using your own timers to call functions has far less overhead than a thread. You run a test against timers and call a function when the condition is hit. The cost and overhead are minimal.

If your game isn't actively running, perhaps you're a very slow-paced game that isn't running at interactive speeds and the processes are mostly sleeping or otherwise blocked, using threads may be more convenient. Also sometimes threads are used for organizational purposes, if you're doing that it may make sense to use them to help wake a task.

 

In general I'd do what you can to avoid creating new threads. Ensuring data flows correctly and timing works correctly is incredibly hard. Multiprocessing introduces an entirely new dimensionality to bugs as race conditions, resource starvation, and deadlocks/livelocks/interdependencies can be quite difficult to untangle and debug.

Timers in games fall into 2 categories: Utilities and in-game/gameplay timers.

Utility timers run on separate threads and trigger system events etc.  An example that used to be common (but should never be done) is a timer to run the game loop.  Timers to update the sound system, to load data, to run physics, etc., are examples of utility timers.  They keep the game running, but are not specific to the game.  They run on system threads.

A game timer is meant to trigger an in-game action.  They are gameplay-critical.  They run in the main game thread and are updated at a specific point within the game loop.  If the game lags, the timers lag.  They can be based on game time, pausable game time, frames, ticks, logical updates, or other game-related timing mechanisms.

 

So which do you need?
Neither. You’re updating an animation.  This is definitely not the purpose of timers.

You draw the correct frame of an animation by determining how much time has passed since the animation began, which you do by simply accumulating it each tick.

If you Tick() for 33333 microseconds, each tick your objects add 33333 to their current animation time (which is stored in microseconds).

Which frame to draw simply depends on how fast they animate.
If I am drawing at 2 seconds in and the animations are running at 24 FPS then I should be drawing frame 48.

Why would you implement a whole timer system instead of a multiply and divide?


L. Spiro

I restore Nintendo 64 video-game OST’s into HD! https://www.youtube.com/channel/UCCtX_wedtZ5BoyQBXEhnVZw/playlists?view=1&sort=lad&flow=grid

In my GUI library I have a separate thread for each timer, but there is rarely more than one. The timers emit events, which can be caught by anyone listening to the timer. Then you simply update or call back or whatever you want based on the seconds passed per tick. The allegro game programming library uses a single thread to manage all timers, but like I said you rarely need more than one to three timers, and you can always base it on the least common factor of the three to achieve the same thing.

 

On 6/11/2018 at 10:01 AM, Shaarigan said:

It dosent make sense to split a sepratate thread for timers if you have 10 other threads running whatever logic in the background.

It is no any sence to make separate timers on Windows/Linux/Android etc. It is not a Real Time OS, so timers precission and threads execution priorities is not warrantied. So it no any ways to make timer more precious then system timer, especially in case timer involves system  messaging queque. Even in case you will setup external atomic clocks to your PC, you can not handle it more preciously than OS system timer becouse can not process its signals at time. So separate thread timers usualy just a way to waste more CPU time.

#define if(a) if((a) && rand()%100)

On Windows, you should create a timer using QueryPerformanceCounter on a separate thread (not a worker thread) and lock that threads affinity to one processor core (NumberOfCores- 1 is a good choice, but using the same core as the games main thread is fine also).  If the timer thread is allowed to transition between cores, QueryPerformanceCounter will return imprecise values on some systems.

IMHO, the primary purpose of OS threads is to allow you to run code on more that one CPU core, which is something that you want to do when you've got code that's extremely computationally intensive.

Waiting for a certain amount of time to elapse is about the least computationally intensive thing you can do, so you shouldn't use threads to solve that problem.

Games are also a little different to typical software, in that we typically have a "game loop" that's running at real-time rates, e.g. 30Hz. The most typical solution for timers in games is to simply poll them once per frame. i.e. for each timer, in the main game loop (once per frame), check if the timer has elapsed and run the associated event if so.

23 minutes ago, ROGRat said:

On Windows, you should create a timer using QueryPerformanceCounter on a separate thread (not a worker thread) and lock that threads affinity to one processor core (NumberOfCores- 1 is a good choice, but using the same core as the games main thread is fine also).  If the timer thread is allowed to transition between cores, QueryPerformanceCounter will return incorrect values on some systems.

This is true on a small number of buggy AMD CPUs from the early 2000's, if they don't have the appropriate patches installed, if they use certain power saving settings, if you're still running on Windows XP or Windows Server 2003.

It's pretty safe to assume that QPC just works on modern Windows. These days, it will automatically select a different implementation that just works, depending on your hardware.

[edit] actually after posting, i re-researched this and found that Intel shipped some CPUs with this same bug as recently as 2014 and Win10 was observed actually selecting the buggy implementation... :(

Do not use threads at all unless you really, really know what you're doing.  Threads are unfortunately sometimes necessary for performance reasons, but the cost of using threads (in terms of extra effort spent programming, extra effort spent testing, extra effort spent debugging, extra bugs that you won't find despite your best efforts at testing and debugging, and even potentially performance) is huge.

This topic is closed to new replies.

Advertisement