Advertisement

Reducing CPU usage in Enet thread

Started by August 20, 2015 11:06 PM
12 comments, last by hplus0603 9 years, 3 months ago
I posted this question over a year ago but didn't find a good soultion.
I use a thread which Enet is created and managed on. The whole thing boils down to 1ms latency vs 100% CPU usage.
In the networking thread I can set the timeout to 1ms, such that it will consume less than 1% of the CPU. But this causes 1ms+ latency, or if Sleep() is used inside the Enet library upto 10ms.
I want to check for new packets every 0.1ms or less, but not consume more then 10% of the CPU core. How can this be done?

I want to check for new packets every 0.1ms or less, but not consume more then 10% of the CPU core. How can this be done?


Non-blocking I/O isn't really needed if you're using UDP and dedicating a thread to reding the socket.
Just have that thread block on receiving datagrams from the socket.

Anyway, your question of latency is interesting.
If you get data "right away" in the receive thread, what would the rest of your program do with it?
If you have another thread that does simulation, the data you receive would have to wait for that thread to simulate response anyway.
And if that's the case, why wouldn't you just poll the network in the simulation thread, right before taking a simulation step?
enum Bool { True, False, FileNotFound };
Advertisement

When I used to use Enet, I did just as hplus0603 suggested, run & update Enet in the main thread as part of the rest of the frame loop.

If I was also rendering, the rendering would naturally limit the framerate & CPU usage, especially if vsync was used.

In case of a headless server, the main loop could sleep to avoid spinning at 100% rate, when it doesn't have simulation work to do. Getting the sleep accurate may be a problem depending on the OS.

Well, polling on the main thread might work if you manage to guarantee that it is done every 5seconds or less, or else the connection breaks. I don't really want a hiccup on the main thread to cause network issues.

Also, as AgentC said, the server side loop spinning makes the server core be at 100% usage unless I put in delays, which I don't want. So either accept 100% core usage or put in Sleep()?

Why would the connection break if you call enet_host_service() (or whatever the name was) every frame? Enet uses its own internal throttling to manage the network data flow. Calling the service function should have very little impact on your program if there is no data available on the wire.

No, if I don't call enet_host_service() for 5 sec it breaks.

Advertisement

Yes, 5 sec could be exceeded in situations like loading a new map.

Some solutions I can see:

- Divide any long operations into smaller chunks of time, e.g. when loading a map, load new entities into it only until a certain amount of ms has been exceeded, then continue on the next frame

- Increase the ENet timeout

- Keep the ENet thread, and implement sleeping on it instead. However I don't think you will consistently achieve 0.1ms response time and low CPU usage, at least not on Windows, due to less than 1ms "sleeps" having to be implemented with spin-waiting instead, but you can use timeBeginPeriod(1) to get the timer (and thus sleep) granularity down to 1ms.

No, if I don't call enet_host_service() for 5 sec it breaks.

Yeah, that is why I suggested you call it every frame. BTW, you can adjust the timeout interval if 5 secs is not what you want.

EDIT: Just read AgentC's post. What I do in cases like loading screens is chop up the loading into as small tasks as possible so the network always gets its share of processing, ie. don't do all loading at once but instead lay it out over several frames. In practice this is crucial for MP titles. What I do in my own engine is hand off the Enet host to a background thread whenever it needs to do something that might potentially block the main thread for a long time (several hundred ms), eg. connection and disconnection are done in a background thread. However, all game state sync and simulation is done in the main loop.

Any reason why blocking reads won't work?

You could have one read-thread and one write-thread for the network..

Any reason why blocking reads won't work?

You could have one read-thread and one write-thread for the network..

Does two threads sharing the same socket/connection work in Enet? Even though one is recieving and the other is sending, is that thread-safe?

This topic is closed to new replies.

Advertisement