Unity optimization tips & tricks: Foreach, the Vampire loop

Foreach, the Vampire loop

This is part 1 out of a 2-part series on foreach loops. Click here for part 2.

What makes code tick? Make code run fast often seems like mysterious, voodoo-like dark magic. But in reality, by using simple tools and our brain we can get you quite far. So today, we’re gonna cover the surprising amount of impact your foreach loops can have on code execution speed.

Under the hood

Picture this: you’re working on a large array of AI data for your latest game, but the performance just isn’t there, no matter how simple you try to keep the functions. So you start profiling your code to see what’s wrong. As you go deeper and deeper, you eventually trace the problem to a single call that isn’t even in the original code: IEnumerator.MoveNext();

So where does this call come from? They are generated from foreach loops. Because, as it turns out, foreach loops are turned into these snail-paced enumerations by Unity’s IL compiler. And as you might know already, enumerations are a prime way of generating extra garbage, slowing down code and other general nastiness. Yes, even generating bugs when you’re not looking!)

As Robert from Somsasin puts it much more eloquently than I can,

foreach (var element in collection) { … }

gets compiled to something like this:

var enumerator = collection.GetEnumerator();

while (enumerator.MoveNext()) {

var element = enumerator.Current;

// the body of the foreach loop

}

That means that for every iteration of the loop, a MoveNext() function is called (and a whole lot of other functions as well), and that’s where the dead weight comes from.

So how do we fix it?

We storm Unity’s head office and picket the place until they cave in and release a bugfix to what should obviously be their biggest concern. Surely they’d fix it. In a more realistic sense though, it’s a problem with Mono moreso than a problem with Unity; so I doubt Unity could fix it even if they wanted to.

So now what? We fix it the old-fashioned way. A foreach can very easily be replaced with a much simpler for, do while or while call. And while I like for loops just fine, I believe performance-oriented code ought to mimic what the processor is actually doing as much as possible. The processor itself obviously has no idea what any of these loops are, it just compares two values, checks and then either jumps or chooses not to jump. This run-code/check-at-the-end method is basically what a do while loop is, and if you add an if statement into the mix, you’ve got a while loop. Now all that’s left, is to keep track of our iteration count, and to fetch any values that foreach would’ve gotten us ourselves. A trivial task. So,

foreach (var element in collection) { … }

becomes

int iterationCount = 0;

while(iterationCount < collection.Length){

var element = collection[iterationCount];

// the body of the foreach loop

iterationCount ++;

}

Using this simple template can boost your code execution speed by a surprising margin, and if you don’t regularly use it already, you should definitely take a look and see if it can improve your code’s execution speed.

1 thought on “Unity optimization tips & tricks: Foreach, the Vampire loop

Leave a Reply

Your email address will not be published.