Coroutines vs Update

Hi guys, I haven’t really used coroutines much, just wondering about performance.

Let’s say you have a unit with a ‘poison’ debuff that lasts for 20 seconds, and damages them every second.

Are you better off using Update(), applying the damage when the time is appropriate (say nextDamage = Time.time + 1 second), or are you better off somehow doing this in a coroutine, perhaps in a while loop?

As a second question relating to this, Is one or the other better for a multiplayer game? I don’t think it would matter either way in this regard, but just thought I would ask.

Thanks.

2 Likes

For something like this which basically applies 1 event 20 times and then 1 a single time you better use coroutines as you can have a simple for loop with yield for 1 second and then just do the debuff and exit of it.

update would take significantly more performance

3 Likes

Thanks, at what point do you think you are better off using Update()? Even the poison was to be applied every 0.1 seconds would coroutines still be better?

I guess at the end of the day Update() is better used for checking input and physics/movement?

Update is then better if you need to check it each frame or if you need more complex branching / state handling where you can’t rely on pausing.

But as long as pausing is the primary thing you do, coroutines are always better.
never forget, an update thats present will be called through a send message alike mechanism which isn’t exactly lightweight, while a coroutine is just there and sleeping :slight_smile:

Especially on more limited devices like mobiles the difference can become impacting if you follow it on all objects

3 Likes

Great, thanks for the tips.

I noticed they used coroutines in the lerp example even to update the enemy units movement, I wouldn’t normally have done this, but I guess there is no input being checked.

Just a few questions extending on this.

  1. If you happen to have an update function running anyway, is it better to use that instead of creating separate coroutines.

  2. Let’s say you are updating the players health, mana and stamina, regenerating them at separate intervals. Is it still more efficient to have 3 separate coroutines using infinite loops to update these values each tick, as opposed to a single Update()? I guess the Update() is still going to run much more often?

  1. no
  2. it’s hard to tell. If you really want to know you will have to test it in your case. I don’t think there will be much of a difference. Coroutines though will result in much better looking code.

Thanks. I guess another option is to have a single constant coroutine loop that runs at a specified interval, every time it runs you could check if it’s time to regenerate the players health etc.

I suppose 1 coroutine should still be better than 3 coroutines, depending on any extra complexity that it takes to manage the 3 tasks.

Anyhow I’ll just create separate coroutines for now, it’s good that I found out about them earlier, otherwise I would have ended up using Update() for everything!

Actually it’s best to use InvokeRepeating where possible. I did a test on 5000 objects, where each one runs this:

private var i = 0;

function Start () {
	InvokeRepeating("Test", .01, 1.0);
}

function Test () {
	i++;
}

That normally takes .03 milliseconds per frame, with spikes of 2.38 ms every second (when all 5000 functions run).

Using this code instead:

function Start () {
	var i = 0;
	while (true) {
		i++;
		
		yield WaitForSeconds(1.0);
	}
}

also takes .03 ms per frame, but the spikes are 4.03 ms every second. Using Update:

private var timer = 0.0;
private var i = 0;

function Update () {
	if (Time.time >= timer) {
		timer = Time.time + 1.0;
		i++;
	}
}

takes an average of .93 ms per frame (no noticeable spikes every second, since the overhead overwhelms whatever CPU time the actual code uses, even multiplied by 5000). At about 100fps, that’s a total of 93 ms per second, whereas the coroutine is a total of 7.03 ms per second and InvokeRepeating is 5.38 ms per second.

–Eric

20 Likes

Thanks for that, seems a bit more elegant for things that are infinitely repeating as well. I suppose coroutines are still useful because of how you can use them.

Just a question relating to the use of InvokeRepeating, if you had a function that was repeating and wanted to change the rate at which was repeating, can you just call InvokeRepeating again on the same function - I assume this will cancel the existing invokation and start a new one?

Yes, coroutines are very useful for scheduling a series of events:

function Start () {
    yield ShowTitleGraphic();
    yield FadeTitleGraphic();
    yield FadeInMainMenu();
    // etc.
}

And other things where InvokeRepeating is too limited. (For one thing, you can’t pass any parameters to functions called with Invoke.)

You’d call CancelInvoke before doing InvokeRepeating again, otherwise you’d have it running twice, at two different rates.

–Eric

2 Likes

Thanks, yeah just realized that.

I guess the performance difference is minimal and coroutines would be somewhat simpler for regenerating health if the rate can change. But usinng invoke just means that you need to re-invoke it when your rate changes, so not exactly a big deal if it means extra performance.

I’m hardly up to the performance optimize stage yet, but it’s nice to know these things so you can do it right to begin with, so thanks for running the numbers on them Eric.

Yeah, basically if you can do the same thing just as easily with either coroutines or InvokeRepeating, then go with the latter. But both of them are a lot better than Update if you don’t need something to run every frame so there’s no point going through contortions to use InvokeRepeating if coroutines are better.

–Eric

I’d like to have a routine run at a high rate, for example repeating every 1 ms, but it seems that InvokeRepeating (“FastUpdate”, 0.0f, 0.001f);
won’t remain constant if the frame-rate is getting low. I take that to mean that the mean that it depends on the Unity Update()… First of all, is that true? Second, could I get around that with coroutines?
not sure if this is the best thread, but it seems like the right people. :slight_smile:

I just started studying Unity Script and the use of Coroutine instead of FSM is something that I’m having some trouble to understand.

The C# compiler converts the IEnumerator into a state machine. Using a Coroutine in Unity is the same as using a state machine, but just easier to read to some programmers. You will still need to verify the conditions on every frame, even if you would like to do something just once.

I understand that callbacks might be usefull here, but can you please explain why running a code in a Coroutine is better for performance than having a FSM in the update function?

1 Like

There is a more elegant way of doing this - the asynchronous way (without coroutines or constant Update checks).

Check out this post on using a Timer.

Note: this approach introduces no performance gain over the Update way of doing things: its purpose is a better written code.

Example code:

public class TimerTest : MonoBehaviour {
 
	private Timer _timer;
   
	void Start()
	{
		Debug.Log("Start");
		_timer = new Timer(1); // tick each second
		_timer.Tick += TickHandler; // subscribe
		_timer.Start();
	}
 
	void TickHandler(Event e)
	{
		Debug.Log("Tick!");
	}
 
	void OnDestroy()
	{
		Debug.Log("OnDestroy");
		_timer.Tick -= TickHandler; // unsubscribe
		_timer.Stop(); // stop the timer
		_timer.Dispose(); // dispose?
	}
}

Tested in visual studio and unity newest version, compiling says

_timer.Tick method does not exist.

You can implement the same logic using either Update or Coroutine. The latter comes with more mechanism to make your code cleaner (if not abused).

If term of performance, they should be nearly equivalent, the only difference would be that extra data structures are required in the background to support coroutines, thought that should be insignificant. Note that coroutines are very likely to be called on every Update, even if you use yield instructions such as WaitForSeconds: nothing is done magically, there must be a check in the background evaluating whether the yield instruction is done or not (see how CustomYieldInstruction works).

Personally, I try to avoid coroutines as much as possible and prefer Update for simple task. I found coroutines not that intuitive and hard to debug, particularly when they are nested, which makes the stacktrace helpless when using break point debugging, leaving me with very few clues about the overall context of execution. For any more complex task, I would organize my code around an FSM or a Behaviour Tree.

1 Like

They’re not, though; Update is called every frame and has some relatively significant overhead for each call. A “yield WaitForSeconds(1)” is quite a bit cheaper vs. running code in Update 60 times (assuming 60fps). While the coroutine still needs to be evaluated every frame, the Update overhead is not involved. It’s better to measure actual performance rather than just guessing based on assumptions. See posts above (from years ago, but still valid…I re-tested it now with 20K objects, and the total time per frame with WaitForSeconds(1) is 0.34ms, vs. 12ms for Update. The coroutines do cause a spike to 30ms as well as GC allocation once per second when the code runs, so that’s something to be aware of).

–Eric

1 Like