How come we don't use Linq for some of these functions?

I’ve noticed this a couple of times before now, but it really stuck out to me in this lecture, so I wanted to ask why we don’t use more of the Linq functions. Is it because the instructors didn’t want to add one more C# language feature when that’s not really the focus of the class? Or is there some reason not to use them in Unity, like performance or something?

Linq creates a lot of garbage, and it’s not something you’d want to do in things like update methods, or places that will be called frequently. Even if you use it somewhere in a function outside of an Update loop, there’s no guarantee that you won’t call it by some wayward means from an update. I suppose it’s easier to avoid this problem if you are not using Linq at all. So, I’d say it’s a performance consideration. Garbage collection does take away some performance

In my own projects, I use Linq a lot, but it’s always on an Event Driven context, never in anything that could wind up in an Update() loop (or called by Update())

Every expression in a Linq query creates a new copy of the collection it is working on… This makes it less than ideal for anything in the main update loop (or any section of a coroutine that loops!). That being said, it’s usually not a problem when called in Start() or in a response to an event.

For example, this would be bad in Update:

var closestEnemy = FindObjectsOfType<Enemy>().Where(e=>e.GetComponent<Health>().IsAlive()).OrderBy(d=>(transform.position - d.transform.position).sqrMagnitude).First();

So each Update, you would be getting every enemy, creating a temporary copy of that resulting collection of enemies on the heap, putting a filtered copy of collection on the heap, then putting a sorted copy of that collection on the heap before finally returning the closest enemy. That’s a lot of GC that we’d be queuing up every frame. That’s faster than the GC can keep up with, so at a certain point, when the available heap shrinks, the GC will wind up blocking to catch up. Not ideal…

But… suppose you had a method that damaged every enemy within 10 units… This might happen as splash damage with a projectile… it would not be unreasonable to have something like this:

foreach(Health health in FindObjectsOfType<Health>().Where(h=>health.IsAlive()).Where(d=>Vector3.distance(transform.position, d.transform.position)<10));
{
    health.Damage(splashdamageAmount);
}

You’re putting 3 copies of the collection on the heap, but since this is not happening every frame, the GC will have no trouble keeping up with it.

Note that Sam will use Linq in Shops and Abilities, but again, in an event driven way, not in Update().

1 Like

Those are good points to think about, thanks for that.

Let me ask about one case in particular though. In the RaycastAllSorted method we created, I used Linq’s Select to make the distance array instead of spinning through manually and doing it.

        private RaycastHit[] RaycastAllSorted()
        {
            RaycastHit[] hits = Physics.RaycastAll(GetMouseRay());
            float[] distances = hits.Select(h => h.distance).ToArray();
            Array.Sort(distances, hits);
            return hits;
        }

So this is clearly going to run most frames, so Linq could be a problem, but it’s already in a context where we are creating more objects, so does that make it ok performance wise?

This is a case where it’s six of one and half a dozen of the other. The sort in and of itself is NOT ideal…
Since you’ve gone to the trouble of using the Linq in the first place, it would be more performant to write this as:

private RaycastHit[] RaycastAllSorted()
{
    return Physics.RaycastAll(GetMouseRay()).OrderBy(h=>h.distance).ToArray();
}

By “more performant” I mean… this is ugly, no matter how you do it, but getting the raycasts and sorting them is somewhat essential to our task. The good news is that this should almost always be a very small number of elements to sort, so not quite as much garbage to collect.

Oh, yeah. Don’t get me wrong. I absolutely love Linq and using it is second nature. But I have to make a conscious effort to not use it in Unity too much. At least not in specific scenarios

Thanks very much to both of you, this has been some helpful information.

This topic was automatically closed 24 hours after the last reply. New replies are no longer allowed.