My game server is eating my CPU

Started by
26 comments, last by rip-off 12 years, 8 months ago
-Your design is correct, 10 is a very small amount of threads, the problem is different.

-60 is too mutch.Interpolate, and use maximum 25 position requests per sec.

-Maybee the loop of your blank cycles eating the performance away? You should try to add some Sleep(4);-s at the while() cycles where you reciving the datas.

-Maybee your database/object/modell/character management is too slow, and eats too mutch cpu?
Advertisement

[quote name='EJH' timestamp='1311012109' post='4836917']
Do you have a "Sleep(1)" at the end the server update function? That should make it much nicer to the CPU.


Sleeping is the wrong way to relieve CPU usage, especially in a network server.

The correct approach is to directly wait on the sockets, either using blocking modes, IOCPs, or select() with a timeout, or whatever. Just sleeping and then busy-polling the socket is incredibly bad design.
[/quote]

Hehe I never suggested busy polling. Sleep (1) in our UDP game server vastly reduced the CPU usage just due to the main server update loop, where game related stuff and outgoing event timers got updated. Nothing to do with polling sockets. :)

Is there anything wrong with that? I mean, a program consisting entirely a single for loop that increments an integer can spike a core to 100%.

I'm doing a Sleep(33) on every thread to regulate to 30 recv()s per second, apparently non-blocking ones.


That's not going to be a very reliable timer. Pretty sure when you sleep(x) the OS doesn't guarantee a return in in x ms, just "at least x" ms. You need a real time running on your server if it is anything more than just a relay.

[quote name='_TL_' timestamp='1311043875' post='4837157']I'm doing a Sleep(33) on every thread to regulate to 30 recv()s per second, apparently non-blocking ones.


That's not going to be a very reliable timer. Pretty sure when you sleep(x) the OS doesn't guarantee a return in in x ms, just "at least x" ms. You need a real time running on your server if it is anything more than just a relay.
[/quote]

Most real-time systems are event driven, and handle events out of a queue. "events" can be things like "data is available on socket X" or "timer Y says it's time to step the simulation."

In these architectures, your main primitives are:
1) pend some event (timer, pending read, etc)
2) wait, blocking, for some event to fire
3) handle the event

You will see that select() is implemented like this, but only works for file handles. I/O Completion Ports on NT, and evented I/O on Linux, works like this as well, and works for a bigger set of objects. A library like boost::asio abstracts the platform specific details and gives you this system using the reactor (for the event queue) and any number of worker threads you want to use to pull events off the queue.

The queue will be blocking -- if a thread asks for work, and there's nothing in the queue, it will be blocked until there is work to do. These kinds of queues are the best way to multi-thread a server in many cases, because you can spawn one thread per CPU core, and make maximal usage of available hardware resources, as long as all I/O is asynchronous/evented as well -- no blocking on file reads in the "pure" model! You have to make sure that your event handlers are all thread safe, though, which may introduce more serialization than ideal, but for some problem domains ("everybody is in the same room") it's hard to avoid.
enum Bool { True, False, FileNotFound };

Hehe I never suggested busy polling. Sleep (1) in our UDP game server vastly reduced the CPU usage just due to the main server update loop, where game related stuff and outgoing event timers got updated. Nothing to do with polling sockets. :)

Is there anything wrong with that? I mean, a program consisting entirely a single for loop that increments an integer can spike a core to 100%.


Yes, there's something wrong with that.

A server should not be doing so much work that it pegs a core, unless there are enough clients actively requesting that much work to be done. If your server registers any non-trivial CPU usage with nobody connected, you're doing it wrong.

Sleeping is also the wrong way to relieve CPU usage. The correct solution is to do less work, or wait intelligently using OS wait primitives where applicable. Sleeping a thread to make the CPU usage drop is very bad, because it's analogous to popping the tires on your car so you can't drive over the speed limit. Sure, the cops will quit pulling you over for speeding, but you're also going to never get anywhere because your tires are flat. As soon as you sleep - even Sleep(0) - to drop CPU usage, your scalability just got axed by an order of magnitude at least. When you do need to use an entire core to do real work, you'll be wasting time yielding CPU for no good reason, just so you chew up less cycles in idle.

The point of idle is that you should use no CPU, not "less" and not "a tiny bit." If nobody asks your server to run the game simulation, you shouldn't be wasting cycles on it.

Wielder of the Sacred Wands
[Work - ArenaNet] [Epoch Language] [Scribblings]

OK, can anybody suggest a method other than Sleep() for regulating server ticks?

OK, can anybody suggest a method other than Sleep() for regulating server ticks?


I already did.
To re-cap:
Either use evented I/O (libevent, boost::asio, etc) with timers for ticks, or use select() with a timeout for when the next tick is supposed to happen.
enum Bool { True, False, FileNotFound };

Sleeping is also the wrong way to relieve CPU usage. The correct solution is to do less work, or wait intelligently using OS wait primitives where applicable. Sleeping a thread to make the CPU usage drop is very bad, because it's analogous to popping the tires on your car so you can't drive over the speed limit. Sure, the cops will quit pulling you over for speeding, but you're also going to never get anywhere because your tires are flat. As soon as you sleep - even Sleep(0) - to drop CPU usage, your scalability just got axed by an order of magnitude at least. When you do need to use an entire core to do real work, you'll be wasting time yielding CPU for no good reason, just so you chew up less cycles in idle.

The point of idle is that you should use no CPU, not "less" and not "a tiny bit." If nobody asks your server to run the game simulation, you shouldn't be wasting cycles on it.


Ok, lets say there are hundreds of timed events that happen at random time intervals on the server. Whenever any of these events occurs, some clients must be informed. What is the proper way to handle that by OS wait primitives? Currently my main server thread looks something like this (Lidgren Network, UDP, 32 player game):



while(1)
{
// (1) update clock
// (2) handle incoming admin commands

// note: 3 through 5 are not necessarily sequential
// (3) handle incoming message traffic
// (4) update all game objects, timed events, etc.
// (5) send any outgoing traffic

// (6) sleep(1)
}


It has low CPU usage always, even with 32 players on 6 year old hardware. Bandwidth would become an issue long before CPU would. But for future reference, I'd like to know the "proper" way to handle things with OS Primitives if you have potentially hundreds of events going out at random times from your server, many of which have no dependence on incoming traffic. Just wondering... in Windows, C# preferably. ;)

Ok, lets say there are hundreds of timed events that happen at random time intervals on the server. Whenever any of these events occurs, some clients must be informed. What is the proper way to handle that by OS wait primitives?


First, simulation events should happen only during well-defined time steps. Time should be measured in "steps" not floating-point seconds. This means that any simulation event always happens during some particular time step.

Second, you schedule notifications to clients. Any event that happens during a time step between the last outgoing packet and the current outgoing packet should be put into the packet being sent to the user. In the simplest implementation, there's a packet per time step per user -- at 30 steps per second, this is quite doable. In more advanced systems, you may have higher step rates (especially if doing precision physics), and you may bundle more steps into a single packet for clients with higher latency, for example.
enum Bool { True, False, FileNotFound };

[quote name='EJH' timestamp='1311187907' post='4838095']
Ok, lets say there are hundreds of timed events that happen at random time intervals on the server. Whenever any of these events occurs, some clients must be informed. What is the proper way to handle that by OS wait primitives?


First, simulation events should happen only during well-defined time steps. Time should be measured in "steps" not floating-point seconds. This means that any simulation event always happens during some particular time step.

Second, you schedule notifications to clients. Any event that happens during a time step between the last outgoing packet and the current outgoing packet should be put into the packet being sent to the user. In the simplest implementation, there's a packet per time step per user -- at 30 steps per second, this is quite doable. In more advanced systems, you may have higher step rates (especially if doing precision physics), and you may bundle more steps into a single packet for clients with higher latency, for example.
[/quote]



This is exactly right.

Discretization is the name of the game. Instead of saying "this event happens in 12 ms, this one in 34 ms" you say "this happens at tick 1+offset 2ms, this one in tick 3+offset 4ms." Then you gather the events that occur in each tick, and send them (along with their tick-relative time offsets) out to the clients. This lets you round-robin updates to multiple clients over the course of a tick, which is a good solution for multithreaded servers; you just tell everyone the timestamp of the tick start and then give them all the information in (authoritative server time) relative offsets.

The server simply runs a simulation tick ahead of what it sends to the clients, so it can aggregate the updates correctly.

Wielder of the Sacred Wands
[Work - ArenaNet] [Epoch Language] [Scribblings]

hplus, can you give an example algorithm similar to EJH's using select() instead of Sleep() and the clock? His algorithm is more or less like mine.

This topic is closed to new replies.

Advertisement