Unity Function Performance Followup
By request, today’s article follows up on my Unity Function Performance article from a year and a half ago using Unity 5.0. It adds on GameObject.SendMessage
and virtual
functions to get a more complete picture of how various function calls in Unity perform. Of course it runs these tests using Unity 5.4 to see if there have been any changes in the engine. Read on for the results!
Today’s contenders are:
- Non-Virtual Instance Functions
- Virtual Instance Functions
- Static Functions
- Lambdas (i.e.
() => {}
) - Delegates (i.e.
delegate(){}
) - GameObject.SendMessage
Here’s the script that runs the test:
using System; using System.Diagnostics; using UnityEngine; public class TestScript : MonoBehaviour { private const int reps = 100000000; private string report; void Start() { var stopwatch = new Stopwatch(); var staticFunctionTime = RunTest( stopwatch, () => { for (var i = 0; i < reps; ++i) { StaticFunction(); } } ); var instanceFunctionTime = RunTest( stopwatch, () => { for (var i = 0; i < reps; ++i) { InstanceFunction(); } } ); var virtualFunctionTime = RunTest( stopwatch, () => { for (var i = 0; i < reps; ++i) { VirtualFunction(); } } ); var lambdaTime = RunTest( stopwatch, () => { Action lambda = () => {}; for (var i = 0; i < reps; ++i) { lambda(); } } ); var delegateTime = RunTest( stopwatch, () => { Action del = delegate(){}; for (var i = 0; i < reps; ++i) { del(); } } ); var sendMessageTime = RunTest( stopwatch, () => { for (var i = 0; i < reps; ++i) { SendMessage("InstanceFunction"); } } ); report = "Test,Time\n" + "Static Function," + staticFunctionTime + "\n" + "Instance Function," + instanceFunctionTime + "\n" + "Virtual Function," + virtualFunctionTime + "\n" + "Lambda," + lambdaTime + "\n" + "Delegate," + delegateTime + "\n" + "SendMessage," + sendMessageTime; } private long RunTest(Stopwatch stopwatch, Action test) { stopwatch.Reset(); stopwatch.Start(); test(); return stopwatch.ElapsedMilliseconds; } void OnGUI() { GUI.TextArea(new Rect(0, 0, Screen.width, Screen.height), report); } public static void StaticFunction() {} public void InstanceFunction() {} public virtual void VirtualFunction() {} }
If you want to try out the test yourself, simply paste the above code into a TestScript.cs
file in your Unity project’s Assets
directory and attach it to the main camera game object in a new, empty project. Then build in non-development mode for 64-bit processors and run it windowed at 640×480 with fastest graphics. I ran it that way on this machine:
- 2.3 Ghz Intel Core i7-3615QM
- Mac OS X 10.11.5
- Unity 5.4.1f1, Mac OS X Standalone, x86_64, non-development
- 640×480, Fastest, Windowed
Notice that this is the same computer that the previous test was run on, aside from the software differences between Mac OS X and Unity.
Here are the results I got:
Test | Time |
---|---|
Static Function | 40 |
Instance Function | 32 |
Virtual Function | 242 |
Lambda | 308 |
Delegate | 304 |
SendMessage | 61539 |
It turns out that GameObject.SendMessage
is ludicrously slow. It’s about 200x slower than the next-slowest type of function. That said, each function type was called 100 million times, so it’s not so slow that you should never use it. Each call was only 6/10,000 of a millisecond, so feel free to make a few of these calls without worrying that it’ll ruin your app’s performance.
Delegates and lambdas are essentially the same and perform essentially the same. They’re a bit slower than a virtual function call, but close enough that you can consider them about the same performance. As mentioned, all three types of functions are about 200x faster than SendMessage
.
Finally there are the “fast” functions: non-virtual instance and static functions. These are again an order of magnitude faster than the virtual functions, lambdas, and delegates. In the previous article the instance functions were a bit slower than the static functions, but that’s been reversed somewhere between Unity 5.0 and Unity 5.4. Regardless, these functions are in the same ballpark performance-wise and blisteringly fast compared to their “virtual” competitors and SendMessage
.
So go ahead and use non-virtual functions to your heart’s content, provided you’re not calling them in huge numbers such as one million per frame. The overhead is really tiny for almost all tasks. Virtual functions, lambdas, and delegates are probably also fine for most tasks. You can certainly make thousands of calls per frame without causing any issues. Just make sure to be careful with garbage creation when using lambdas. Finally there is GameObject.SendMessage
that you should use extremely sparingly. A few per frame is OK, but never use them heavily or you’ll quickly run into performance problems.
Do you ever use SendMessage
? Do you avoid virtual functions, lambdas, or delegates? Let me know how it’s worked out for you in the comments!
#1 by Andy Buchanan on October 13th, 2016 ·
Have you tested IL2CPP vs Mono?
#2 by jackson on October 13th, 2016 ·
This test was on Mac where there is no IL2CPP support yet. It’d certainly be nice to get IL2CPP stats for articles like this one and some readers have asked for it, so perhaps I’ll start testing both scripting backends in the future.
#3 by David Barlia on October 13th, 2016 ·
I wonder how delegates would score if, instead of invoking 100,000,000 times, the call were added to the delegate 100,000,000 times and then (start the stopwatch now…) invoked once. While that may be an unfair comparison, given the removal of the overhead of the for loop, not *having* to iterate is one of the innate advantages of delegates.
#4 by jackson on October 13th, 2016 ·
I changed the lambda part of the test to this:
Then ran it on the same computer as in the article. After five minutes or so I killed the test app and lowered
reps
to 100,000. Again it took several minutes and I killed the test app.Suffice to say it’s a bad idea to add tons of listeners to a delegate. For more on this, check out this article where I discuss C# event performance in depth. Since events are basically delegates, it makes sense that their non-linear time growth would apply here too.
#5 by David Barlia on October 14th, 2016 ·
Wow! That was not what I was expecting at all. And thank you for the link to your other article– What a revelation!
…Well, now surely you’ve got to add UnityEvent to this speed test.
#6 by Justin Wasilenko on December 10th, 2018 ·
David I just tried this benchmark and UnityEvents are much faster then SendMessage and C# even faster.
Performance running 100 million reps:
Mono, (Ticks)
C#Event, 178
UnityEvent, 1482
Messenger, 3820
Event System – Dispatcher, 4134
SendMessage, 42248
Tested on Windows Standalone 640×480, Fastest, Windowed, Unity 2018.3.0f1
#7 by Stephen Hodgson on October 27th, 2016 ·
Jackson, you sir are a gentleman and a scholar!
Thanks for all this great information.