Unity’s coroutine support allows you to easily create pseudo-threads and write synchronous-looking code that doesn’t block the rest of the app. They can be very handy for a variety of tasks. Before using them, we should understand the performance cost. Today’s article takes a look at the cost of starting a coroutine as well as the cost of running it. Just how expensive are they? Read on to find out!

Coroutines are just C# iterator functions. That means they return a System.Collections.IEnumerator and have at least one yield return X statement in them. Here’s one that moves a GameObject toward a destination every time it’s resumed:

IEnumerator MoveToDestination(
	GameObject objectToMove,
	Vector3 destination,
	float speed
)
{
	// Not at destination yet
	while (objectToMove.transform.position != destination)
	{
		// Move toward destination
		objectToMove.transform.position = Vector3.MoveTowards(
			objectToMove.transform.position,
			destination,
			Time.deltaTime * speed
		);
 
		// Yield new position
		yield return objectToMove.transform.position;
	}
}

You could manually iterate over this function, but Unity can do that for you by starting it as a coroutine. If you do, it’ll be resumed every frame just like your Update function. To start it as a coroutine, all you need is a MonoBehaviour and to call the StartCoroutine function on it like so:

class MyScript : MonoBehaviour
{
	void Start()
	{
		// Pass the IEnumerator the coroutine function returns
		// to the StartCoroutine function
		StartCoroutine(MoveToDestination(gameObject, Vector3.zero, 5));
	}
 
	IEnumerator MoveToDestination(
		GameObject objectToMove,
		Vector3 destination,
		float speed
	)
	{
		// ...
	}
}

Now to test its performance. I’ve set up a small script that starts one thousand, ten thousand, or one hundred thousand coroutines that do nothing but yield. They’re the cheapest coroutine you could write. They also start up as fast as possible, which is good because I’m measuring the time it takes to start up all the coroutines. From there on I display the frame rate the app is running at. Here’s the code:

using System;
using System.Diagnostics;
using System.Reflection;
 
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
 
public static class StopwatchExtensions
{
	public delegate void TestFunction();
	public static long RunTest(this Stopwatch stopwatch, TestFunction testFunction)
	{
		stopwatch.Reset();
		stopwatch.Start();
 
		testFunction();
 
		return stopwatch.ElapsedMilliseconds;
	}
}
 
public class TestScript : MonoBehaviour
{
	private Rect drawRect;
	private bool showModeScreen;
	private long startTime;
 
	private const float UpdateInterval = 1;
	private float totalTime;
	private int numFrames;
	private float timeleft;
	private float fps;
 
	void Start()
	{
		drawRect = new Rect(0, 0, Screen.width, Screen.height);
		showModeScreen = true;
	}
 
	void OnGUI()
	{
		if (showModeScreen)
		{
			GUI.Label(new Rect(0, 0, 200, 25), "How many coroutines?");
			if (GUI.Button(new Rect(0, 25, 100, 25), "1,000"))
			{
				StartTest(1000);
			}
			else if (GUI.Button(new Rect(0, 50, 100, 25), "10,000"))
			{
				StartTest(10000);
			}
			else if (GUI.Button(new Rect(0, 75, 100, 25), "100,000"))
			{
				StartTest(100000);
			}
		}
		else
		{
			timeleft -= Time.deltaTime;
			totalTime += Time.timeScale / Time.deltaTime;
			numFrames++;
 
			if (timeleft <= 0)
			{
				fps = totalTime / numFrames;
				timeleft = UpdateInterval;
				totalTime = 0;
				numFrames = 0;
			}
 
			GUI.Label(drawRect, "Start Time: " + startTime + ", FPS: " + fps);
		}
	}
 
	private void StartTest(int numCoroutines)
	{
		showModeScreen = false;
 
		var stopwatch = new Stopwatch();
		startTime = stopwatch.RunTest(
			() => {
				for (var i = 0; i < numCoroutines; ++i)
				{
					StartCoroutine(CoroutineFunction());
				}
			}
		);
	}
 
	private IEnumerator CoroutineFunction()
	{
		do
		{
			yield return null;
		}
		while (true);
	}
}

If you want to try out the test yourself, simply paste the above code into a TestScript.cs file in your Unity project’s Assets directory and attach it to the main camera game object in a new, empty project. Then build in non-development mode for 64-bit processors and run it windowed at 640×480 with fastest graphics. I ran it that way on this machine:

  • 2.3 Ghz Intel Core i7-3615QM
  • Mac OS X 10.10.2
  • Unity 4.6.3, Mac OS X Standalone, x86_64, non-development
  • 640×480, Fastest, Windowed

And got these results:

Number Start Time FPS
1000 6 650
10000 75 121
100000 814 8.2

Unity Coroutine Test Performance Graph

First off, starting coroutines is cheap. It’s not free either, but unless you’re suddenly starting hundreds of them in a single frame it probably won’t matter. In cases where you’re tempted to do that, like making a bunch of game objects all move to a destination at the exact same time, you should probably consider spreading that work out over multiple frames or not using coroutines.

As for the actual coroutine performance, we see here that it’s certainly possible to drive the frame rate into the ground with nothing but empty coroutines. In this sense, the overhead is enough to make them expensive. On the other hand, a hundred thousand coroutines is probably not a realistic number. But even 1000 is much higher than the test computer’s frame rate with no coroutines: about 740. There is definitely some expense to them above and beyond simple function calls.

So if you’re looking for maximum performance, coroutines aren’t for you. However, they are really quite cheap when used in small numbers. A few dozen shouldn’t be much of an issue for most games.

Do you have any thoughts to add on coroutines in Unity? Love ’em? Hate ’em? Had performance troubles? Post a comment!