Coroutines are not magic

I wanted to try to get this message out clearly and broadly.

Lately we have seen a number of very convoluted uses of co-routines in questions in Unity Answers and the question always seems to start with the same statement:

“I used co-routines because I didn’t want to slow my code down with code in Update…”

Be very clear. There is no magic in coroutines.

The reason loading your Update down with unnecessary work slows your program is because that work gets done every frame. If you run a co-routine at the same rate doing the same work, you have gained nothing. Worse, you have added a context-switch overhead to each run and potentially made the problem worse.

There is no magic to either Update or Co-routines. The point of the warning about Update is that you should avoid polling, anywhere, at all costs.

4 Likes

What about update being called through reflection? I have heard that it’s slow in this way. I’m not sure how this would compare with coroutine overhead. Does this reflection get used every frame, or does it get used once and then it’s like calling a normal function every frame after?

Either way, it’s probably rather insignificant as a performance cost. And this question probably sounds quite dumb, but I’m not very experienced with how reflection works at this point.

Even though there is a overhead in Unity for built-in magic methods like Update, it is certainly not the kind of optimizations that should be used.
To me it is just another example of premature optimization from inexperienced programmers who try to find the absolutely best and fastest method, but fail to see the big picture.

3 Likes

A coroutine running every frame is slower than Update running every frame. It can still make sense to use coroutines every frame (e.g. it’s trivial to delay stuff when needed, unlike Update), but speed isn’t one of the reasons in that case.

–Eric

1 Like

The latter. It’s not quite as cheap as calling a function in your own C# code, but it’s about as cheap as native-to-managed calls can be.

1 Like

I don’t know the internals of Unity, but I have written other code that is based on reflection.

The slow part of reflection is generally looking up and finding the method. Once found, it is very cheap to invoke.
Well written code that has to call the same method over and over reflects once to find it and then caches it. i have to believe Unity is at least doing that. Seeing as they have control over the VM they may be doing other Unity specific things to optimize as well.

I use CoRoutines sometimes and did indeed see performance gains when I ran code every ~3rd frame from a CoRoutine. Although Garbage Collection might have been higher. That was way back on Unity 3 though. Subscribing to see what others say.

1 Like

Funny you say that, because coroutines are actually implemented with ‘magic’. Sure… it’s just a compiler generated state machine, but until you’ve dug into it it is indeed magic.

Sure, if you’re code is exactly the same as an Update. However Coroutines lend to a different kind of thinking which can be quite beneficial - an optimisation in paradigm can be worth many in pure cycles. An update cannot wait for seconds, it cannot be stopped or paused, it cannot be scheduled, it cannot easily represent a state machine etc.

I agree with your main point - a coroutine that simple functions like an update in and of itself has no noticeable performance gains for any practical purpose I can think of. However, there are differences and understanding these differences can lead to a more nuanced response.

Did you compare that to running something every third frame with Update? I’m guessing you’d find the Update way was faster, if you profiled it.

Which should be the main lesson in this topic: Don’t assume something is an optimization. Profile it.

Depends on what you’re doing; if the work is small, then it can be dominated by the cost of invoking the Update function.

Note that InvokeRepeating() should be cheaper than a coroutine if you’re always waiting a constant interval between doing work.

The advice about profiling is sound though.

1 Like

They are magic dammit! Why can’t some people just believe in the magic?! There are tons of young wizards and sorceresses out there and you are shattering their reality! :frowning:

2 Likes

You can easily do all of that in/with Update…

Coroutines use a well defined and reliable mechanism from .net. On the other hand Update, FixedUpdate and all the other built-in methods are much more magic at least from a technical point of view. They are based on reflection and it is extremely easy to get into a situation where you are not getting a single error message pointing you to the cause of the issue. Those situations may e.g. be that you use “OnColisionEnter” or “update”.

InvokeRepeating is one of the ugliest methods that exists in Unity, because it only allows you string based invoking. It would need to be significantly faster than any other approach, before I would even consider to use it.

Performance is not the only thing that matters.

Not really…you can implement logic so that the effects are similar, but it’s not as easy as coroutines.

InvokeRepeating is indeed faster than Update and coroutines, and while having to use a string is annoying, it’s the simplest possible method of making something happen at timed intervals.

–Eric

It is trivial to create a non-string based InvokeRepeating using coroutines.

Can’t think of a single use for a coroutine.

Then you’re not thinking very hard.

Sure, but that does take extra work and no longer has the performance benefit.

–Eric

3 Likes

K.

What I meant is that it is trivial to create a reusable InvokeRepeating that only needs to be implemented once. You just spend that little amount of work once.

If I can decide between a little bit of performance and compile time warnings, I will always prefer the latter. With every error that can be caught at compile time, we can avoid a lot of debug time.

I just looked it up in the docs, it basically says coroutines are there for.convenience.