Zero-Overhead async/await for Unity by custom async statemachine with C# 7.0
Unity supports async/await from Unity 2017, it is great feature but not used widely now. Why? The main reason is framework(Unity) does not provide await support to AsyncOperation. This is not all, standard Task operations is thread base, it is not match to Unity. Finally performance, Task can not avoid some overhead.
I’ve created library that resolve there all issues. This is the sample code of it.
UniRx.Async! Yes, I am the author of UniRx and today I’ve released a new version(v6.0.0) on Unity Asset Store and GitHub releases page. Update details, please see UniRx/releases/6.0.0 but here is not talk about UniRx.
UniRx.Async is an independent feature with UniRx, It can add without UniRx(in releases page, providing UniRx.Async without UniRx unitypackage).
UniRx.Async provides many features based on custom async statemachine builder, it does not use Task and SynchronizationContext. Completely built on continuation.
Overhead of standard Task
Task has many functions, it deals with threads, and performs asynchronous marshalling that is platform independent. Okay, platform independent(used in Windows Forms, WPF, ASP.NET in Web, Xamarin, etc…) is great but of course trade-off of optimization.
Anyway, let’s try to see the details by Profiler.
This details spans two frames of await frame and completed frame.
Task is checking ExecutionContext many times but UniTask omit completely.
UniTask is more simple, less overhead and less garbages.
What is UniTask? UniTask consists of a struct value holder and a custom async state machine builder. Custom async state machine builder can controls how handle async/await in C#. So I decided to create zero-overhead async/await system that tight-coupled with Unity.
Unity C# is run on C++ Game Engine and it driven by single-thread. For example AsyncOperation.completed += action event is invoked by engine and it is already returned main-thread. That is, basically no SynchronizationContext is needed.
Frame Operations
UniRx.Async requires C# 7.0 because task-like custom async method builder is C# 7.0 feature. Currently Unity compiler is C# 6.0 but if you use IncrementalCompiler in Package Manager, to be enabled C# 7.0.
If you encounts unknown errors, please try to downgrade IncrementalCompiler version.
When move from coroutine, we notice async/await has no frame operations. yield return null, yield return WaitForEndOfFrame, etc. And the Task.Delay is thread-pool based timer operation, it is too heavy and not suitable for Unity.
UniTask provides frame based Delay and Yield.
// continue after next 100 frame.
await UniTask.Delay(100);// continue after PostLateUpdate
await UniTask.Yield(PlayerLoopTiming.PostLateUpdate);
There are work on PlayerLoopSystem introduced in Unity 2018(experimental). It can await EarlyUpdate, FixedUpdate, PreUpdate, Update, PreLateUpdate, PostLateUpdate, you can choose own timing.
There are completely no use thread. So does not need SynchronizationContext marshaling cost and perfectly works on WebGL.
Rx vs coroutine vs async/await
Simply I say async/await is the best to handle asynchronous operation.
However, standard async/await can not replace Rx and coroutine because it is very poor to use in Unity. UniRx.Async can be replaced by many functions.
Rx has many sides and functions. Rx’s event streams and reactive programming is still good. Asynchronous to use async/await, Event to use Rx. We can use both so it is the better paradigm.
The future of UniRx
I must apologize to everyone. Long time does not update so one asked is UniRx dead? Meantime, I’ve released commercial-game(Black knight and White devil in Japan iOS/Android market) as CTO of company. And built libraries to use, MessagePack for C#, Utf8Json, MagicOnion etc. Last month, I’ve left company and founded own company called New World, Inc(currently doing consulting for C#, Game Development(Unity), support my OSS libraries. If you interested, please contact me).
Today, I’ve rebooted UniRx. I should maintain and improve to support great game development and to prove power of C#. Sorry, I did not watched many issues and PR! I’ve started checked and triage issues.
Both UniRx and UniRx.Async can add many features. I’ll keep development.
I've started Open Collective, https://opencollective.com/unirx . If you want to support development, you can donate/become a bucker/become a sponsor. If I can create development time many more, will be very happy.