Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Coroutines are not magic

Discussion in 'General Discussion' started by Jeff-Kesselman, Nov 18, 2014.

  1. Jeff-Kesselman

    Jeff-Kesselman

    Joined:
    Apr 5, 2010
    Posts:
    99
    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.
     
  2. JasonBricco

    JasonBricco

    Joined:
    Jul 15, 2013
    Posts:
    956
    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.
     
  3. Dantus

    Dantus

    Joined:
    Oct 21, 2009
    Posts:
    5,667
    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.
     
    Last edited: Nov 20, 2014
    kaiyum, deram_scholzara and Robota like this.
  4. Eric5h5

    Eric5h5

    Volunteer Moderator Moderator

    Joined:
    Jul 19, 2006
    Posts:
    32,401
    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
     
    Nanako likes this.
  5. superpig

    superpig

    Drink more water! Unity Technologies

    Joined:
    Jan 16, 2011
    Posts:
    4,649
    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.
     
    JasonBricco likes this.
  6. Jeff-Kesselman

    Jeff-Kesselman

    Joined:
    Apr 5, 2010
    Posts:
    99
    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.
     
  7. jerotas

    jerotas

    Joined:
    Sep 4, 2011
    Posts:
    5,572
    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.
     
    Nanako likes this.
  8. npsf3000

    npsf3000

    Joined:
    Sep 19, 2010
    Posts:
    3,830


    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.
     
  9. wccrawford

    wccrawford

    Joined:
    Sep 30, 2011
    Posts:
    2,039
    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.
     
  10. superpig

    superpig

    Drink more water! Unity Technologies

    Joined:
    Jan 16, 2011
    Posts:
    4,649
    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.
     
    wccrawford likes this.
  11. GarBenjamin

    GarBenjamin

    Joined:
    Dec 26, 2013
    Posts:
    7,441
    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! :(
     
    SunnyChow and RJ-MacReady like this.
  12. Dameon_

    Dameon_

    Joined:
    Apr 11, 2014
    Posts:
    542
    You can easily do all of that in/with Update...
     
  13. Dantus

    Dantus

    Joined:
    Oct 21, 2009
    Posts:
    5,667
    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.
     
  14. Eric5h5

    Eric5h5

    Volunteer Moderator Moderator

    Joined:
    Jul 19, 2006
    Posts:
    32,401
    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
     
  15. Dantus

    Dantus

    Joined:
    Oct 21, 2009
    Posts:
    5,667
    It is trivial to create a non-string based InvokeRepeating using coroutines.
     
  16. RJ-MacReady

    RJ-MacReady

    Joined:
    Jun 14, 2013
    Posts:
    1,718
    Can't think of a single use for a coroutine.
     
  17. Eric5h5

    Eric5h5

    Volunteer Moderator Moderator

    Joined:
    Jul 19, 2006
    Posts:
    32,401
    Then you're not thinking very hard.

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

    --Eric
     
    npsf3000, flaminghairball and lmbarns like this.
  18. RJ-MacReady

    RJ-MacReady

    Joined:
    Jun 14, 2013
    Posts:
    1,718
    K.
     
  19. Dantus

    Dantus

    Joined:
    Oct 21, 2009
    Posts:
    5,667
    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.
     
  20. RJ-MacReady

    RJ-MacReady

    Joined:
    Jun 14, 2013
    Posts:
    1,718
    I just looked it up in the docs, it basically says coroutines are there for.convenience.
     
  21. Eric5h5

    Eric5h5

    Volunteer Moderator Moderator

    Joined:
    Jul 19, 2006
    Posts:
    32,401
    Yes, convenience...making things easier/faster to code is a big benefit. Unity itself is there for convenience; you could just create your own engine from scratch, after all.

    --Eric
     
  22. imaginaryhuman

    imaginaryhuman

    Joined:
    Mar 21, 2010
    Posts:
    5,834
    I think of coroutines are more of a scheduling thing than a multitasking thing... it lets you pause execution and come back some other time to carry on... so it does effectively give cpu time over to some other routines to use, but only in terms of directing the flow of traffic, not reducing the traffic. It's like cooperative multitasking, where you proactively hand-over control to some other piece of code, rather than being `pre-empted` ie interrupted at any point in time. From what I understand, they also only run on one cpu core. If coroutine were multithreaded that'd be different.
     
    NickHaldon and npsf3000 like this.
  23. RJ-MacReady

    RJ-MacReady

    Joined:
    Jun 14, 2013
    Posts:
    1,718
    I'll think about them, see if they can save me some time.
     
  24. Dameon_

    Dameon_

    Joined:
    Apr 11, 2014
    Posts:
    542
    If by similar, you mean exactly the same from an end-use perspective...

    As far as easier, checking elapsed time is quite possibly one of the easiest things I can imagine, involving a couple of variables and a quick subtraction. Three lines of very transparent code, max. So three lines instead of one.

    Unity programmers seem pretty liberal with coroutines, though, using them wherever they want any kind of delay. Personally, I'm pretty sparing in my coroutine use. They're a specific tool for a specific job.
     
  25. Eric5h5

    Eric5h5

    Volunteer Moderator Moderator

    Joined:
    Jul 19, 2006
    Posts:
    32,401
    Exactly.

    Not writing extra code is even easier than that.

    --Eric
     
    Deleted User and flaminghairball like this.
  26. Breyer

    Breyer

    Joined:
    Nov 10, 2012
    Posts:
    412
    I think there is one case where coroutines are very helpful (compared to "common" solution) update something once per frame while ur code may call, lets say, SetDirty mulitiple times in one frame from different sources. Because coroutine have builtin new WaitForEndOfFrame so you could fire coroutine with update function on first call and later - only check if coroutine was fired if yes reject next SetDirty
     
    Last edited: Nov 19, 2014
  27. 0tacun

    0tacun

    Joined:
    Jun 23, 2013
    Posts:
    245
    I once tried to write my AI in Update() with many timers and it became quickly confusing. Then I switched to coroutines and now use them more often for code, I don't need to call 60x per second.
     
  28. 3agle

    3agle

    Joined:
    Jul 9, 2012
    Posts:
    508
    This thread is peculiar.
    I'm fascinated that so many people don't understand coroutines, it's fairly common in programming.

    Contrary to many here, we rarely find a use for the Update function, certainly less often than coroutines that's for sure.

    Oddly, we do use Update for AI, though our AI requires* constant attention each frame, so I can imagine some AI systems would function better outside of it.

    * (an overstatement really, since it could/should have been implemented to not require it)
     
  29. Meltdown

    Meltdown

    Joined:
    Oct 13, 2010
    Posts:
    5,816
    You've never needed to use a yield to wait for some condition or some other event to happen and then continue executing in that same method? Sounds fugly..
     
  30. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,617
    To add to Jeff's message, you shouldn't try to make optimizations anywhere without:
    a) fully understanding the system you're optimizing within, and;
    b) measuring the performance of the thing you're optimizing both before and after.

    If you think something is "magic" then you don't understand it and, at the very least, run the risk of "optimizations" that make your overall code worse and, in the long run, slower.

    I know a guy who's job is to optimize other people's code to run on given target platforms. Often he gets significant speed gains by removing the so-called "optimizations" of those who came before him.
     
  31. Nanako

    Nanako

    Joined:
    Sep 24, 2014
    Posts:
    1,047
    as far as i understand, coroutines should be used for big "one-off" calculations, which would cause stuttering if you attempted to do them in a single frame.

    if there is work to be done in every frame, then yea it's pointless. But if your work is done once per second, spreading that out over 60 frames could help. or just as many frames as needed, which is probably a lot less than that many.
     
    Last edited: Nov 20, 2014
  32. Eric5h5

    Eric5h5

    Volunteer Moderator Moderator

    Joined:
    Jul 19, 2006
    Posts:
    32,401
    Actually that's not really a good use of coroutines. Coroutines aren't threads, so you would have to arbitrarily decide how to split a calculation over a period of frames, which may not solve the problem. Plus it makes the code framerate-dependent...how long does 60 frames last? It could be 1 second, or more, or less. This sounds like one of those "optimizations that's really not".

    What coroutines should generally be used for is what they are: state machines, as already discussed. It's trivial to direct program flow using coroutines. (The flipside is that if you're careless, you can end up with coroutine spaghetti, where stuff happening depends on other stuff happening in undefined ways.)

    --Eric
     
  33. hippocoder

    hippocoder

    Digital Ape

    Joined:
    Apr 11, 2010
    Posts:
    29,723
    I rarely use them. I just code timers in update. I think coroutines are a convenience if you don't want to manage timers and a bit of extra logic. I can't see the overhead being any different. There might be allocations though.
     
    randomperson42 likes this.
  34. CDF

    CDF

    Joined:
    Sep 14, 2013
    Posts:
    1,307
    Coroutine's are great for programming sequences, or as some would say, state machines. I use them all the time for that purpose.
     
  35. npsf3000

    npsf3000

    Joined:
    Sep 19, 2010
    Posts:
    3,830
    Then I fear you don't understand co-routines... see some of the other responses in this thread.
     
  36. Nanako

    Nanako

    Joined:
    Sep 24, 2014
    Posts:
    1,047
    You split it by time? check how long it's been going after each little piece of the task, if it's nearing some threshold based on the target framerate, then yield execution and try to finish next frame, nothing arbitrary about that, or framerate dependant. repeat until done with expensive task.

    am i wrong/
     
    Last edited: Nov 20, 2014
  37. Nanako

    Nanako

    Joined:
    Sep 24, 2014
    Posts:
    1,047
    I would like to know more about this use case, actually. to me it sounds like quite a massive convenience. And it'd be really nice to have some more specific information on the overheads between the two.
     
  38. Eric5h5

    Eric5h5

    Volunteer Moderator Moderator

    Joined:
    Jul 19, 2006
    Posts:
    32,401
    Sounds like it would be a lot simpler and more efficient just to use threads for that.

    --Eric
     
  39. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,617
    While it's not something I'd recommend planning on, I did once do exactly this very successfully. We had to move an editor-only function to runtime on short notice and it was pretty heavy so we knew we had to make it asynchronous. We decided on a time budget per frame (based on measured performance at our minimum target spec for that part of the simulation), modified the function so that it measured its time usage and yielded appropriately, and moved it into a coroutine.

    Having said that, it wasn't intended as an optimization. We did it to provide functionality that would otherwise have taken significantly longer to achieve. If we'd planned up front for that functionality to be in runtime rather than Editor-only we'd have designed and implemented it differently in the first place.
     
  40. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,617
    That's how I achieved what I mentioned in my last post. However, there are better ways to do it if you have the chance to design for them earlier in the pipeline.
     
  41. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,617
    Coroutines allocate. Running your own timers in Update() does not. Running a timer in Update requires polling in Update, where coroutines do not.

    Personally, if I'm running an Update anyway, I'll use that where I can. If I'm not running an update anyway, it's a pretty hefty thing to add just to have timing between some method calls.
     
  42. Nanako

    Nanako

    Joined:
    Sep 24, 2014
    Posts:
    1,047
    Simpler? I dispute that on the subjective basis that i have no idea how threads work. Googling "unity threads" does not bring up any official documentation, for a start. And unity's api is apparently not thread-safe. I have no idea what that means but it sounds scary and confusing. Simplicity is right out the window.

    I have no doubt that you're right on the efficiency though.

    is there maybe some tutorial/guidelines on the general concept?
     
  43. JasonBricco

    JasonBricco

    Joined:
    Jul 15, 2013
    Posts:
    956
    I've used coroutines for a situation such as: I have a loading screen that fades to clear (it's just a panel with loading text on it at the moment). When a specific function is called, I want to fade the loading screen to clear. And I use a coroutine to fade the loading screen because this needs to happen over several frames until it's done, and then stop. Once it's done, I want to resume inside the function that called it. But not until the loading screen is faded to clear entirely.

    So I make the function a coroutine, and then within that I call:

    yield return StartCoroutine([function that fades the loading screen]);

    This waits until that coroutine is finished and the screen is faded to clear, then resumes after.

    Is this bad use of coroutines? I couldn't think of a better way to do it at the time.
     
  44. npsf3000

    npsf3000

    Joined:
    Sep 19, 2010
    Posts:
    3,830
    I would point out that there's no inherent reason for coroutines to not be multithreaded. Obviously it makes some things more complicated, and the Unity API isn't set up for it so I don't expect to see an official version soon. However I have implemented coroutines on a server before - threaded, delays, WaitForMessage etc. In many ways it was nicer than the Unity version - e.g. this worked:

    Code (csharp):
    1. yield return 150; //Wait for 150 milliseconds
    Which I liked. Of course this can be done in Unity and if you make your own implementation you can design it as you like :)
     
  45. hippocoder

    hippocoder

    Digital Ape

    Joined:
    Apr 11, 2010
    Posts:
    29,723
    As far as I know, kicking a coroutine off will create some sort of allocation, but it won't (have not confirmed) for InvokeRepeating.

    I use invoke and cancelinvoke here and there in a previous title for shockwave effects. This thing needed to spawn an additional shockwave every so often, so I used InvokeRepeating for this purpose. After the effect completes, it cancels. I've used it for spreading fire as well.
     
  46. Eric5h5

    Eric5h5

    Volunteer Moderator Moderator

    Joined:
    Jul 19, 2006
    Posts:
    32,401
    System.Threading

    Running a thread to do basic stuff is pretty trivial, and if you're doing lengthy calculations you probably aren't using the Unity API anyway. Or if you are, it's typically stuff that you only need to use after the calculations are done. Example: doing complex stuff with mesh data, then when that's finished applying the end result in the main thread.

    Nope, that's pretty much exactly what coroutines are good for: scheduling a sequence of events.

    --Eric
     
    RJ-MacReady likes this.
  47. RJ-MacReady

    RJ-MacReady

    Joined:
    Jun 14, 2013
    Posts:
    1,718
    Meh. Reminds me of something Bjarne Stroustrup wrote in one of his C++ books, knowing language features doesn't make you a good programmer, or something to that effect. If you can achieve the results in a non-hacky way you're A-O.K. in my book. ;)

    I bet if you looked under the hood of some of your favorite games, you'd be like... "well I'd have done that differently". :/

    Seems a waste to argue about such minute efficiency differences.
     
  48. RJ-MacReady

    RJ-MacReady

    Joined:
    Jun 14, 2013
    Posts:
    1,718
    Points for looking up something in documentation... why don't more people do this?
     
  49. Jeff-Kesselman

    Jeff-Kesselman

    Joined:
    Apr 5, 2010
    Posts:
    99
    So, the point has drifted a bit.

    There is no doubt that, in the right circumstances, coroutines can be a useful thing for organizing your code.
    In normal C# code, co-routines can be particularly useful when combined with a threading pool.

    But the point was that using them just because you have Update-phobia is not a good idea.

    Update is called as part of the core, optimized Unity game loop. Furthermore, unlike co-routines, update calls do not require a context-switch so the basic over-head call will be lower.

    And I don't believe you can do multi-threading on the C# level in Unity. It will get very upset if you try as AFAIK none of the script APi is thread safe, This is why Unity has its own mechanism for starting and running co-routines. Those are being run on the gameloop thread.
     
  50. lmbarns

    lmbarns

    Joined:
    Jul 14, 2011
    Posts:
    1,628
    If you never use co-routines, what do you do when you need to yield while waiting for something to finish? Like when using WWW class, System.IO, and a number of things?