Threading question

Started by
14 comments, last by King Mir 9 years, 6 months ago

Would making the member variables of the components atomic also be an option?

"I would try to find halo source code by bungie best fps engine ever created, u see why call of duty loses speed due to its detail." -- GettingNifty
Advertisement
I hope you appreciate this, a mandatory introduction into C++ in regards to its memory model:

http://channel9.msdn.com/Shows/Going+Deep/Cpp-and-Beyond-2012-Herb-Sutter-atomic-Weapons-1-of-2

I strongly recommend you spend the time watching it. I can not emphasize it enough: MT is not the land where you can just try what works and what does not - you need to understand what is going on (ie. the hows and whys). The linked talk is a good start i would say.

You can also establish a thread to perform a synchronous blocking Listen or Read from a socket, this will couse no CPU usage from the thread while the thread is sitting on the blocking function. You then from other thread can issue data to this socket so the pending thread will start working, and you can peek for result on a non blocking socket from the issuing thread for data being happend to be delivered. This is simply having no shared memory, but instead swap contacting data between workers, what allows for non-blocking, performant fullfillment of issued command on a spare thread. If you happen to have a lot of data being in a shared state, you can narrow down the contacting, keeping only synchronization between the thread's internal copies of data.

Having a graph of scene/game objects with individual locks on them is bad juju. It leads to a completely non-deterministic game loop where individual systems are operating data from this frame and the previous frame, as they all fight for locks and update your spaghetti mess of data at random intervals. Atomic members is even worse, as class invariants that affect more than one member can no longer be enforced, and individual objects can now be in a half updated/half stale state.

You shouldn't use locks or atomic variables, except in the core guts of the threading system of your engine where absolutely needed.

Most engines are based instead around data-flows.
e.g. Take some code:
A = Task()
B = Task2(A)
C = Task3(A)
D = Task4(B,C)
you can look at the input regions and output regions of these tasks, and their dependencies between each other.
Task1 has no dependencies, and treats data block A as an output. Task2 depends on Task1's output as it's input. Task4 depends on the outputs of Task2 and Task3.
Once the dependencies are known, you can arrange the tasks into a Directed Acyclic Graph, and can use a topological sort to generate a linear schedule that the tasks can be executed in.
Each task might be made up of a list of parallel jobs.
In this example, all engine threads can execute the list of Task1 jobs, then sync to ensure the whole list is completed, then they can execute all of the Task2 and Task3 jobs, then sync again, then execute the Task4 jobs.
No locking. No atomic members in high level code. Just scheduling of execution.
The high level game code remains completely deterministic. You scale across any number of CPU cores. There's no deadlocks or race conditions possible. It's more efficient due to a tiny number of sync points / atomic memory accesses (compared to one in every lock).
There's a reason that job pools have become the defacto standard way to scale across multiple cores in game engines. If you're touching locks, you're doing it wrong! :D

Would making the member variables of the components atomic also be an option?


In short, no. Atomic would make access to those variables incredibly slow and disable a lot of valid optimizations the compiler could otherwise do. Not to mention that your code using these variables is going to want to modify more then one, or make changes based on the state of multiple variables and you're right back to where you started, except that instead of seeing corrupted variable data you'd see corrupted class/struct data.

Would making the member variables of the components atomic also be an option?

No. Atomics are an optimization over locks for small types. But if you have several related fields in an object, those fields need to modified together so that they stay consistent. To ensure that happens with atomics you would need to make the whole class one atomic unit, which it most cases makes it not be a small type. Using atomics with any type, means you can't access individual members, but rather must read or write the whole object at once, which can make even small structs inefficient as atomics when you really do only need one member.

Atomics are an option when you're only sharing a small result, finding the average of a bunch of integers.

This topic is closed to new replies.

Advertisement