It’s a mouthful of a title but it should be good for searches
Ok, so I’m sure many veteran C#'ers know this already, but everywhere I look, when ever people are talking about Coroutines in C# I see this…
IEnumerator myAwesomeCoroutine()
{
while (true)
{
doAwesomeStuff();
yield return new WaitForSeconds(waitTime);
}
}
What I wanted to point out was that using “yield return new WaitForSeconds()” causes a guaranteed 21 bytes of garbage allocation every time due to the “new” part (compared to the standard coroutine 9 bytes you would get with a “yield return null”).
To avoid this, simply set up your wait times in advance…
WaitForSeconds shortWait = new WaitForSeconds(0.1f);
WaitForSeconds longWait = new WaitForSeconds(5.0f);
IEnumerator myEvenAwesomerCoroutine()
{
while (true)
{
if (iNeedToDoStuffFast)
{
doAwesomeStuffReallyFast();
yield return shortWait;
}
else{
dontDoMuch();
yield return longWait;
}
}
}
Now your coroutine will only cause the bare minimum 9 bytes GC allocation each time it is called (not including other allocations you might be causing through your code of course!).
However, someone who isn’t aware that caching objects, instead of constantly instantiating copies, is better for GC isn’t going to know to search about garbage collection tips for coroutines I’d bet =/
Could be right there, I was just assuming searching for “coroutine” or “garbage collection” might just put in in the list and be useful to someone who didn’t even realise they wanted to know it
The only problem I can foresee with using pre-instantiated WaitForSeconds objects is if you tried to use one in more than one coroutine at the same time. It may have a way to handle that case, but my guess is it would cause timing to get screwed up in both coroutines.
Speaking of GC allocation and optimization, is it a big deal if there’s a small amount of allocations such as 4kb/frame? Is it necessary to put in the effort to optimize?
How about a ~50kb allocation maybe once every couple seconds?
As I could gather the YieldInstructions (WaitForSeconds, WaitForEndOfFrame and WaitForFixedUpdate) are handled by the coroutines just as a delimiter or conditional break. each coroutine must be handling internally the wait logic especially the elapsedTime (WaitForSeconds).
Therefore creating a new WaitForSeconds(1.0f) object at t=0 and then creating another new WaitForSeconds(1.0f) at t=1 causes the same effect as sharing the object in a cached value.
Hence they are not bound to the gameobject itself nor time of creation it is safe to share them between objects.
I have implemented it in my current project using coroutines for almost everything (AI loops, bullets, explosions, slow-mo effects, UI updates and such), the shared Yielders have not caused any bug or issue and have reduced the GC Alloc considerably.
@Crayz each shared/cached YieldInstruction will save just a few bytes (not a big deal), in the other hand creating them once they are needed will cause problems as Coroutines can be used heavily by multiple objects on multiple consequent frames and then released (once the coroutine has continued execution) the continuous allocation of YieldInstructions will trigger the Garbage Collector to clean the HEAP memory, THIS is the real issue here, GC is expensive and most of the time the only reason to have FPS drops or game update glitches.