0

I have a DirectX game which spawns 2 boost threads on a dual-core system: 1 for gameplay/rendering (normally split into their own threads on a quad-core CPU), and 1 other thread which procedurally-generates the gameworld. I believe that my audio middleware also spawns its own threads for playing SFX and music.

The game is always running at 100% on the CPU, which in turn can cause some sputtering from the audio system. I'm hoping that I can reduce the CPU load by better managing the activity of that Generation Thread. While sometimes I need it running full-speed, there are other times (when the player isn't moving much) when it is just updating constantly while not really doing a whole lot.

Is it possible / advisable to manually manage how active a thread is? If so what strategies can I use to do that? I keep seeing people say that sleep() functions aren't really recommended, but I don't really know what else to do yet.

Alternatively, am I barking up the wrong tree by trying to squeeze cycles out of thread management, and would I be better served by traditional profiling/optimization?

Raptormeat
  • 312
  • 2
  • 14
  • 4
    Ask yourself a simpler question first: What would it mean for a simple, single-threaded program to *not* run at 100% CPU load? How do you make a program *not* run? – Kerrek SB Jan 31 '14 at 22:46
  • You are always better served by initially performing traditional profiling/optimization. – Skyler Saleh Jan 31 '14 at 22:47
  • "when it is just updating constantly while not really doing a whole lot." Do you mean the procedural generation is just constantly recalculating the same results? If so then what you want to do is have the procedural generation code wait until it needs to generate something new. – bames53 Jan 31 '14 at 22:51
  • @KerrekSB - I 100% hear you, and I admit that the topic is poorly-thought out and communicated, but did you read the question? I've got a thread which sometimes is sort of "spinning" without doing much and my question is about whether there's any utility in shutting that thread down or deprioritizing it to give other threads a chance to get more work done. That said, I do definitely understand your point. – Raptormeat Jan 31 '14 at 22:55
  • @bames53 yeah, pretty much. How to best make it wait, though? Is that a time when using sleep() is the right thing to do? I don't know if that's going to help me in the greater picture, but I'm just trying to understand what the best practices are with this kind of thing. – Raptormeat Jan 31 '14 at 22:59
  • You're basically asking "what's the best way to slow down a car from the outside that has the gas pedal pushed down all the way". The problem isn't that the car is travelling too fast. The problem is that the gas pedal is always down all the way. What event causes your loop to do real work rather than than useless spinning. Is there a way you can "block" on that event? – hatchet - done with SOverflow Jan 31 '14 at 23:05
  • @hatchet- yes there is (and thank you for that edit). As in my original question, what's the best way to do that? Calling a sleep() function? – Raptormeat Jan 31 '14 at 23:11
  • @Raptormeat: Usually games tell the OS to send them an event every 16ms. Each time they receive the event they update the world and draw to the screen, and then wait for the event again. – Mooing Duck Jan 31 '14 at 23:20
  • @Raptormeat: I did read the question. I'm trying to provide some guidance that will get you to thinking about interactive systems in the right way. If your answer ends up with epoll, timerfds and eventfds (or some Windows analogue), we're getting somewhere. – Kerrek SB Jan 31 '14 at 23:32
  • @KerrekSB, well, forget about the "100% CPU" and "reducing the load" nonsense. Instead I should have just said, maybe, that I have one thread that is sometimes spinning while not accomplishing much, while it seems like my audio thread is struggling to keep up. I'm suggesting that perhaps better managing that situation (thread priority? sleeping?) might give the audio thread a chance to catch up. I have a general inexperience with thread management best practices. From that starting place, is there any more guidance you might be able to give? – Raptormeat Feb 01 '14 at 00:32
  • @Raptormeat: I'd start everything from the other end: You would typically "respond to events" in some way, and the crucial part is the "waiting for new events" call, which sleeps until there's something to do. That way your process doesn't use any more resources than it actually has work to do. Events are either I/O (network, console), or UI activity, or timers. You can have multiple threads wait on the same event source, say, so that the UI remains responsive while a frame of graphics is rendered. – Kerrek SB Feb 01 '14 at 01:28

3 Answers3

5

Getting to 100% processor utilization means that you don't have a game clock. You are probably rendering frames as fast as the machine allows. Still pretty hard to get 100% exactly if you use multiple threads, that indicates that you don't synchronize threads either.

This is likely to require a pretty drastic rewrite. The pace ought to be set by the main render loop, the one that copies the back-buffer to the video adapter. It sets your target FPS, frames-per-second. Not infrequently, you use the vertical blanking interval of the monitor for that, it solves tearing problems by ensuring that the monitor gets updated at the exact right time. Which automatically gets the render loop paced to the monitor refresh rate. Typically 60 times per second on LCD monitors. A timer is an alternative. This prevents the main thread from burning 100% core, assuming it can keep up with the FPS.

You now have a steady game clock tick, discrete moments in time at which things need to happen and jobs need to be completed in order to update the game state. Like checking for player input. Inside the render loop, check for mouse/keyboard/controller input and use anything you get to update the game world objects.

And in turn determines what worker threads need to do. They'll have the duration of one pass through the render loop to get the job done they need to do. You use a synchronization object to wake them up. And another one, each, to let them signal that they are done with the current game loop tick. Which stops them from burning core, they should constantly be waiting on the signal to start working on the next frame. Note that there is a balancing requirement. If a worker thread needs more than one game tick to get the job done then the render loop will fall behind and miss a video adapter frame update. Your video starts to stutter. This is in general impossible to eliminate completely, do make sure that it doesn't affect the absolute game clock.

Audio should be the easier problem to solve, you just need to keep the sound card buffers filled with sufficient data to survive a couple of frames worth of sound.

Falling behind on the target FPS is very easy to determine. You automatically compensate for that by lowering the target FPS. So the program still runs acceptably on a slow machine, just not as smoothly. Net effect is that you'll stop burning 100% core on all threads.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
1

I don't know anything about your system so this answer may be way off. I assume that there is such a thing as audio output buffer of some sort that you can track the size of. When the size of the buffer is so small that there is a danger that audio may stop you should do something to refill the buffer.

That "something" may be as easy as temporarily setting the priority of audio thread to higher value. Come to think of why not set it higher from the start? That would solve all the problems, right? Even better, just lower the priority of world generator thread.

Dialecticus
  • 16,400
  • 7
  • 43
  • 103
  • Thank you for the answer- yes this is probably a much smarter approach. Unfortunately I don't have access to the audio buffer/thread, so maybe normal optimization / reevaluating my middleware might be the best approach. Regarding thread priority- is that something that is API dependent, or platform dependent? The first thing I've found is the SetThreadPriority() function on windows. Am I on the right track? – Raptormeat Jan 31 '14 at 23:18
  • @Raptormeat I don't see the difference between an API and a platform. Windows SDK actually used to be called Platform SDK earlier. But yes, SetThreadPriority is the function. Not really sure why you accepted the answer, though, because it seems you would like more ideas. Questions with accepted answer usually have no more answers. – Dialecticus Feb 01 '14 at 00:06
  • Sorry for the confusion, when I said API, I was thinking about the Boost threading library which is what I use to create my threads. Either way, that's something I can just figure out on my own. As to your other point- you are right so I unaccepted the answer for now. I guess I was just pessimistic about getting a better answer! – Raptormeat Feb 01 '14 at 00:11
0

When I worked on a voice-comm app for gamers many years ago, we hit this problem a lot. Many games are written to use every ounce of CPU. As such, some gamest would starve our app (that ran in the background) from functioning - causing audio drops and lost network connections. Many of those games would also call SetThreadPriority and SetPriorityClass with the REALTIME flags to basically consume all the CPU quantums with disregard of anything else running on the system.

The typical fix we asked of game developers we partnered with was to simply insert a "Sleep(0)" call between each frame of their main game loop so that our threads wouldn't get stalled. I think we later added a switch in a software update to make our process run at a higher priority mode. Since then, Windows has gotten better about multitasking and thread priority with respect to these issues.

selbie
  • 100,020
  • 15
  • 103
  • 173