Asynchronous (Async) Programming, sometimes referred to as Parallel/Multi-threaded Programming, is a form of computer programming that allows for work to be completed across multiple cores in parallel. Async is something that has become more important as CPU speeds have been slower to increase, but the number of Physical CPUs (Cores) has increased. This means while your CPU may not be growing in speed as fast as they were 10 years ago, there are more of them on the average processor. Most Processors today contain 4 physical cores (with 2 being the next likely) Source: Steam Hardware Survey.
So, how can we use those cores? And what does that do for us? Well, first let's take an example and see how it applies to both standard (Synchronous) and Asynchronous techniques.
For this test, we've created an Ability in Able called "Self Destruct". This Ability has no targeting and simply does a sphere query, copies those results to the Ability Context, and then deals damage to all targets. We'll put our character in a level, surrounded by 32 enemies and then fire our Ability and use Unreal's Profiler (which Able has built in support for) to chronicle our times.
A few quick notes before going to the results:
Now, onto the results.
You can see here, our Max time hit 0.3 milliseconds (for reference, if your framerate target is 30FPS then you have ~33ms per frame to do your work in. If your target is 60FPS, you have ~16ms per frame). Not bad overall, especially when you consider this is what is happening under the hood:
The engine is running all our calculate damage calls one after another on a single core. If our calculate damage for actor call was more expensive, or we targeted more enemies - this time would only increase. Again, this behavior is perfectly fine and our time isn't that bad, but let's see what we can do if we apply a bit of Async work to our Ability.
A ~23% drop in time! Let's look at what's going on under the hood with this call:
There's a bit of overhead with using Async due to the setup, so our simple damage calculation is probably just on the cusp of being not worth it - but we still saved some time by farming things out to other cores so we could quickly move on to simply applying the damage and just asking the various cores for their result when we're ready for it. Pretty good considering this was done by simply checking a box on the Task!
To understand that question we need to look at a couple ways Async work is completed within Unreal Engine 4 and, thus within, Able.
Both versions require some overhead in wrapping up everything for the worker threads, so that's also something to be aware of.
Async is a powerful system that can improve performance, but that power requires a bit of care in handling.
Here are some helpful guidelines.
So, we've taken a very broad look at what Async is and how it can help performance as well as given some tips for its use. Finally, Able was written from the ground up to make use of Async whenever possible, both at the Ability and Task level of execution.
Some examples of Async supported features in Able are:
More tasks and further optimizations are being made all the time, so this list will continue to grow as Async isn't going away anytime soon and more and more engines are adopting it as a feature required in gameplay systems to reach the performance and scalability that is expected from their customers. If you still have questions, please check out our Tutorials/FAQ Page, or our Documentation.