These kind of reactions sounded extreme to me, and "it's slower" doesn't tell you much, so I wanted to actually measure it.
I created an empty Unity project, and created a scene with 2100 static objects arranged in a grid. Each object is a textureless sphere, and has one component called PerformanceTest. The component is written in C#. This component contains a start() method, and another method that does a few useless things just so it's not empty: Allocate a Vector3(), check its magnitude, multiply that result up and down a few times. This method is either Update() or UpdateInvokeRepeating() depending on the test. I also ran a test with no update method present at all, just an empty start() method.
When InvokeRepeating is used in this test, their timings and delays are all set the same, to make the difference as obvious as possible. With 2100 of these in the scene it's pretty easy to see results in the Unity profiler. These were tested on a Ryzen 1600 running at 3.3 GHz.
Nothing Startup: CoroutinesDelayedCalls 3.78ms, containing PerformanceTest.Start() 1.26ms
Update() Startup: CoroutinesDelayedCalls 3.78ms, containing PerformanceTest.Start() 1.26ms
InvokeRepeating() Startup: CoroutinesDelayedCalls 5.23ms, containing PerformanceTest.Start() 2.75ms
You can see here that upon startup, calling InvokeRepeating 2100 times cost approximately 1.5ms. The other two startup methods were present, but totally empty. Since the method name is provided as a string, this is the point where a search will have to happen, so this could potentially get slower if you have a lengthy class.
Nothing Update: BehaviorUpdate 0.0ms (of course)
Update() Update: BehaviorUpdate 2.34ms, containing PerformanceTest.Update() 0.92ms
InvokeRepeating() Update: CoroutinesDelayedCalls 3.44ms, containing PerformanceTest.UpdateInvokeRepeating () 0.94ms
Stacking all the InvokeRepeating calls does indeed lose to a straight Update, with the overhead appearing to be roughly 50% higher than Update has. Notable is that CoroutinesDelayedCalls is not present in the list on frames where no invocations occur, and the Unity generic "Overhead" does not meaningfully change either. There isn't an obvious ongoing performance penalty between invocations.
What this suggests to me is that unless your repeated invocation is more frequent than every other frame, you're probably getting a net win by using InvokeRepeating rather than Update() if possible. While that may not be worth it if you need to have the overhead of Update() being present anyway, it doesn't have a dramatic startup cost and its additional overhead is easily overcome if your component doesn't need to update every frame.
No comments:
Post a Comment