Post

About C# LINQ Performance

About C# LINQ Performance
TL;DR — Key Takeaways
  • LINQ Count() has significant overhead versus for/foreach - never use it in per-frame calls
  • Filtering (Where) and transformation (Select) perform close to for/foreach
  • LINQ creates intermediate buffers (arrays), increasing GC pressure - frequent calls can cause GC spikes
  • Use LINQ for prototypes/non-critical paths, and for/foreach for per-frame or performance-critical code
Visitors

Table of Contents




Advantages of LINQ

  • LINQ (Language Integrated Query) is a C# feature set for writing concise and convenient code.
    Developers can use query syntax to perform filtering, sorting, and grouping on data sources
    with minimal code.


Notes When Using LINQ

  • Most LINQ operators create intermediate buffers (a kind of array), and all of that becomes garbage. 👉 GC occurs
  • Even when one or two LINQ operators are enough, splitting logic into many LINQ calls for readability can degrade performance.
  • Operators such as Average, Count, OrderBy, and Reverse iterate the source sequence, so they are less efficient from a performance perspective.


So before using LINQ, think through these rules.


Usage Rules

  1. Is this a situation where rapid implementation matters (prototype, TDD)? - Use LINQ
  2. Is this code not performance-sensitive? - Use LINQ
  3. Is LINQ’s convenience/readability truly needed here? - Use LINQ
  4. Is this called every frame or performance-critical? - Do not use LINQ



LINQ Performance Benchmark



Benchmark - Loops

Return the number of Customers whose age is below 18

For / Foreach
// Performance: fast (no GC)
int count = 0;
foreach (var c in customers)
{
    if (c.Age < 18)
        count++;
}
return count;
LINQ Count
// Performance: slower (GC occurs)
// Count() internally
// iterates the entire sequence
return customers
    .Count(c => c.Age < 18);
  1. For / Foreach linq1

  2. LINQ Count / Predicate Count linq2

linq3

Compared to for/foreach, LINQ has some overhead (indirect extra time, memory, and resources for functionality).
Filtering then counting is relatively close to for, but plain Count that returns sequence length shows notable performance degradation.



Benchmark - Filtering

Return a list that is a subset of customers with age 18 or higher

  1. For / Foreach linq4

  2. LINQ linq5

linq6

It shows very similar performance to for/foreach implementation. About 4ms slower than for/foreach.



Example code actually used when filtering RaycastAll results and checking tags

linq7

For filtering scenarios, LINQ made the code much more concise and readable than typical for/foreach.


Benchmark - Transformation

Return a list of customers where age is represented in months instead of years

  1. For / Foreach linq8

  2. LINQ linq9

linq10



How Much GC Does LINQ Create?

See the link below for test conditions.

Result

  1. GC Alloc Count linq11 linq12

Microsoft Docs: Performance Recommendations for Unity

Performance recommendations for Unity

linq13



LINQ vs For - Performance Comparison Chart

Relative execution time (lower is faster, for = 1.0 baseline)

Conclusion

  1. LINQ Count is significantly slower.
  2. Filtering (Where) and transformation (Select) are similar to for in performance.
  3. LINQ allows very concise one- or two-line code.
  4. Debugging is harder.
  5. GC allocation occurs; overusing LINQ can become hard to manage.
  6. Avoid LINQ in per-frame calls or other performance-sensitive paths.
This post is licensed under CC BY 4.0 by the author.