C++ Multithreaded to the Extreme

Diconica

Well-Known Member
Apr 25, 2020
1,092
1,138
Update:
While the method below does give a lot of performance in the long run I can't say it is the best option.
I left the original post below for people to see because it does have some lessons that can be learned from it and in part is still used but to a lesser extent.
I started by modifying the primary loop in the worker threads so they are state managed.
This gives me a massive amount of added control.
Such as I can use the threads differently at different times. Example would be I can use the threads to pull from a queue when loading content similar to the method below. During some operations I can simply divide tasks up and load balance work loads on worker threads. I can drop threads into a sleep state based on the game state or a pause flag and so on.

The work load is divided by using an ID for each thread and thread count and then it splits the load on each thread.


Original Post Below:
So last night I finished building the base of my game engine.
Sean Parent has a good youtube video
In this video he discusses performance and issues with various ways of multi-threading and how to get the most out of performance.
The following code is what he is talking about in effect in the video.

It's fast real fast. However, I wouldn't say it was the best design to fit with my game engine or for ease of use and management.
Th e biggest part I took from his video was the multiple queues, task sharing and stealing.

I created a single class rather than multiple classes to handle the multiple queues rather than one class for each.
This allowed me to effectively emulate a single queue with the performance of multiple. It also allowed me to build in a few features that made live easier.
I created a better way to track if the queues were empty or not by using an atomic counter. It's also thread safe vs the empty function of queues isn't.
I use two variables to set the index for which queue to load next and which one is to be pulled from.
The gettask function has a means by which if the queue it is indexed to is ever empty it will move on to test all the queues up to 3 times . This simulates Parents method in the task system or trying to rotate through the number of queues.

I also changed the task system to something more like the following.


The primary thing to take away from this is when the queue is empty the task threads go into a clear wait state;
I made the condition variable on the queue system public so as that it could be used for this.

I also built in a number of features to the system that allow me better manual control and make it work better with the game engine.
Such as the ability to clear the queues at any time, to stop the system, tell how many tasks are left on queue.

I designed the system to have a primary management thread. This allows me to take the standard single engine game and add this to it and with little effort convert the game to multi threaded with little effort.
Lets say I am updating a lot of troops they are in an array. I can break the update system up using a for loop and wrapping the guts of it in a lambda and placing it in the queue.
In most cases the troop number would be divided by a multiple of the worker thread count.

I am considering making a friend function that can auto manage this to some extent to increase performance. Some times breaking something up more is better sometimes less is better.
That will be my next part to work on.

Source code:
I'm choosing not to post it. I'll explain why.
First, it took about a solid day to figure out what I needed to make this work well with my game engine. I already explained what most of that was above. That means you won't need to do that. Most of that came from observation. Such as seeing the management thread out run the worker threads. Yes, it can load up task far faster than the workers can pull them.
They actually have to run them. So being able to keep an eye on stuff like that and being able tell how and when to pause the system and give it the ability to catch up. Such as using task IDs of a sort to ensure they all ran before reloading... So there are enough tools to handle that and more. But not an over bloated amount.

I also posted the code that is similar to some extent what I have and told you what I took away from it.
IF you can't build effectively what I did in a day or two based on what is given above it is improbable you would understand my code and be able to use it if I did post it.
Two ways you can look at the time building your version. It's a right of passage. Or you could look at it as the opportunity to familiarize yourself with it and code it in the style you like.
You can also write all the notes in you like.
My code is divided into two files 103 lines and 113 lines a 3rd of that is just blank lines to make it easier to read.
That's not a lot of code. As you can see in the image below. It's really not made with others in mind.
You don't have permission to view the spoiler content. Log in or register now.
If you have questions with in reason I'll try and help.
I guess you could use a program to try and figure out most of that. Of course some of it is barely legible and some you can guess at.
I wouldn't waste time on that. By the time you figured it out you probably could have wrote it yourself.
 
Last edited:
  • Like
Reactions: cbsx