2

Edited the original question to elaborate: I'm using unity coroutines to perform some heavy duty operations. Unity is a game engine and codes are run within frames. If an operation is intensive, it has to be done in coroutines otherwise it takes a long time for a frame to complete. Here the coroutine method is DoTasks. If you are not familiar with the unity engine, they are similar to iterators.

First of all I have to say these codes work as they are supposed to do. The problem is with heap allocation. That said I will explain what the codes do. When Init is called it starts the coroutine and goes into DoTask and then goes inside foreach to iterate currentTask.Execute() and then goes into obj.CreateCellGosTask to iterate. now the first yield return we encounter, the chain of foreachs return the result to the initial coroutine(namely StartCoroutine(DoTasks()) ) and we're done for the frame. On next frame the code continues in the chain right after the last line that was executed. This is the behaviour and works fine.

public class TaskScheduler : MonoBehaviour
{
  private static volatile Task currentTask;
  public void Init(){
     StartCoroutine(DoTasks()); //Starts the coroutine
  }
  private IEnumerator DoTasks()
  {
     while(true){
       foreach (object b in currentTask.Execute())
       {
         yield return b;
         //Do something
       }
     }
  }



public class Task
{
private Cell cell;    
public IEnumerable Execute()
{
    foreach (object b in cell.CreateCellGosTask()){
      yield return b;
    // Do something
    }
}

What the yield returns is of zero importance. In all nested iterators it yield returns null.

The problem is about heap allocation. The code creates garbage due to the fact that the compiler generates hidden classes implementing IEnumerable (I think). Unfortunately Garbage collection is a big deal in unity.

The ultimate goal is to zero heap allocation in foreach chain (StartCoroutine is not important).So the question is exactly what code the compiler generates and How it creates Enumerable and Enumerator classes? I mean the exact generated code of DoTasks and Execute. Then I could just type the exact same code and create and return a struct instead of a class.

morteza khosravi
  • 1,599
  • 1
  • 20
  • 36
  • 1
    You need to provide a [mcve] of your code. – Enigmativity Jan 14 '16 at 08:41
  • 1
    You can find this video useful for better understanding coroutines https://www.youtube.com/watch?v=ciDD6Wl-Evk . Also you can use decompiler and look inside of UnityEngine.dll – Dmitry Morgachev Jan 14 '16 at 11:29
  • 1
    @DmitryMorgachev It's not about classes. The class is generated at runtime and the only clue I have is that it is an IEnumerable. Also I have no knowledge about IL so I can't understand what is the actual implementation – morteza khosravi Jan 14 '16 at 11:47
  • 1
    hi morteza. As I explain below, it's very likely that **coroutines are not at all what you want here**. It is quite common that experienced engineers like yourself, who are new to unity, aim for the wrong points such as this ..... one possible reason could be Unity's incredible lack of explanations of anything in the documentation! :O – Fattie Jan 14 '16 at 16:08

1 Answers1

4

(You may prefer to skip to my short explanation in large letters below!)

I may misunderstand what you're trying to do, but,

1) Coroutines have absolutely nothing to do with threads.

(Unity does not use threads at all. If you need to make a thread (say for processing) you need to use a thread manager (there are many available, or write your own) ... but it is unrelated to coroutines.)

2) Coroutines have no return value. You just yield return null to skip a frame or break when you're done..

Some notes,

http://answers.unity3d.com/answers/966469/view.html http://answers.unity3d.com/answers/1119978/view.html

that was a discussion about "how do you call 'the result of' coroutines more than once" which is kind of related to what you're asking. (That came up when I myself was asking this ... https://stackoverflow.com/a/34550206/294884 ... which I certainly did not realise!)

I hope this helps in some way!

Just finally

4) You can't nest coroutines in any meaningful way.

You're just "starting yet another new coroutine". You know? What you're referring to is either just "waiting until" one finishes to run another one, or, "going ahead" and starting a few up at a time.

Google 100s of discussions on this .. http://answers.unity3d.com/questions/14081/nested-coroutines.html or http://answers.unity3d.com/answers/515074/view.html

You can't meaningfully "nest coroutines" in any way.

Imagine you have a kitchen table with stopwatches sitting on it. You start and run a stopwatch. If for some reason you want to, you can start and run many of them. (Some of them "may start the others" themselves, or they may be started from elesewhere.)

But there's no concept of "nesting" them, they're just stopwatches running there.

Don't forget, all you're saying is "it is code that will run each frame" - nothing more than that. (Exactly like Update().)

Once again ----- I get the feeling that what you're actually after is threading in Unity, which can be achieved with care. Example ---

http://answers.unity3d.com/answers/443604/view.html

Indeed you sort of want nothing to do with the whole frame system, nor coroutines, sounds like you need a thread perhaps with a math calculation.


To be absolutely clear .....

Just to repeat the same point,

public class TaskScheduler : MonoBehaviour

Note that coroutines very simply

have no connection, at all, to "tasks" or "threads"

a "coroutine" is nothing more than this:

a way to run something every frame.

That's all it is. As you know, the game engine environment gives you a "each frame..." concept run loop.

Let's say for whatever reason (say .. moving an object, animating a monster) you want to do something "every frame". There are two ways to access that ability in Unity.

(1) Just use the Update() quasifunction which Unity provides for you:

Update()
   {
   if ( moveTheDinosaur )
     {
     // code here will run every frame,
     // frames are beautifully managed by Unity
     {

(2) Just use a coroutine:

launch coroutine showDinosaur
coroutine showDinosaur()
  {
  while(true)
     {
     // code here will run every frame,
     // frames are beautifully managed by Unity
     yield return null;
     // the formulaic line "yield return null"
     // indicates to the MonoBehaviour engine that's
     // the end of your processing this frame;
     // (Implementation details are unknown to us
     // and irrelevant)
     }
  }

Note that - indeed - if you're an experienced programmer as soon as you use Unity for more than a day, you realise the "Update()" thing is generally speaking completely silly, you tend to just use your own coroutine to do something each frame. (Of course, "Update()" is handy just for quick demos or whatever when testing code.)

Again to reiterate, couroutines just have no connection to "tasks" or "threads" -- which -- I guess, of course I may be wrong -- is what you're getting at. Coroutines are simply how you access the "frame system" in Unity. For threading in unity, look in to one of the many threading-pool-helper type scripts or systems available, which are convenient.

Community
  • 1
  • 1
Fattie
  • 27,874
  • 70
  • 431
  • 719
  • Well, thanks for the trouble of a very long and comprehensive answer. Actually some important pieces of the algorithm is multithreaded but still unity API is single threaded so there is no way to pull huge operations like setting terrain heightmap or creating colliders in a multithreaded context and also thread time slicing is somewhat big on android devices. It's not Angry birds it's a open world project and took 1 yr to the day. To squeez every millisec on unity's main thread I have to use nested coroutines. otherwise I can't have a fine control on chunking intensive tasks. – morteza khosravi Jan 14 '16 at 17:06
  • 1
    hi morteza. sure, if you have some processing to do, **use a thread**. I suggest you grab all the threading packages such as https://www.assetstore.unity3d.com/en/#!/content/20857 Coroutines **have no relation to threading** and **will not help you in any way**. Note that **all coroutines run on the main thread exclusively** so that is absolutely not what you want. – Fattie Jan 14 '16 at 21:38
  • 1
    Here is an outstanding post by the best Unity engineer, http://answers.unity3d.com/answers/357040/view.html where someone was confused coroutines V threading, and this explains how to use threading perfectly. – Fattie Jan 14 '16 at 21:39
  • 1
    note that there is simply **no such thing** as a nested coroutine. – Fattie Jan 14 '16 at 21:39
  • I like the whole concept you are suggesting and the link you provided is completely relevant. But some jobs have to be done in the main thread, namely terrain's SetHeight and SetAlphamap and they are quite expensive. Of course there is a way to avoid nesting coroutines but this pattern is much simpler than any alternative and doesn't intrude your current methods. you just make them IEnumerable and call them with foreach. I call "Task" everything that needs to be done on the main thread. These tasks are sometimes complicated chains of method calls. I start a Task on TaskScheduler class. And – morteza khosravi Jan 15 '16 at 02:56
  • then the task goes on its way and executes some work. During Task execution I check the time spent to see how much time was taken running Tasks. If we have consumed ,say 10 ms, then we are done for the frame and it will yield return null. And on next frame it has to be resumed exactly where it stopped on last frame. This way I have perfect and absolute control on when to freeze the Task and let the frame finish. – morteza khosravi Jan 15 '16 at 03:09
  • Then you need a coroutine that checks DateTime.Now or use Stopwatch.ElapsedMilliseconds and will yield return null once a certain value is reached. – Everts Jan 15 '16 at 05:43
  • @fafase Yes. It currently uses Time.realtimeSinceStartup to measure elapsed time – morteza khosravi Jan 15 '16 at 13:06
  • @JoeBlow Actually I use terrains for many reasons including very nice LOD system and support for billboarded trees and very good collider performance. I've never seen any asset comparable to unity's terrains so I think they are the best way to go. – morteza khosravi Feb 06 '16 at 03:00