-
Notifications
You must be signed in to change notification settings - Fork 5.3k
Description
Description
LINQ is one of the most beloved features of C#, offering declarative and readable data manipulation. However, for high-performance scenarios, developers are often forced to abandon LINQ in favor of manual foreach or for loops to avoid allocation and virtual call overheads.
I propose that RyuJIT (utilizing Dynamic PGO) should be enhanced to recognize and optimize common LINQ patterns on standard collections (List<T>, T[], Span<T>), effectively treating them as intrinsics.
The Problem
Even with recent optimizations in System.Linq, using LINQ still incurs costs:
- Allocations: Delegate allocations (unless static), closure allocations, and enumerator allocations.
- Interface Dispatch: Iterating via
IEnumerable<T>orIEnumerator<T>involves virtual/interface calls that block inlining. - Missed Vectorization: The JIT cannot easily vectorize a LINQ chain compared to a raw
forloop.
Currently, "Clean Code" (using LINQ) is often at odds with "Performant Code".
Proposed Solution
With the advent of Dynamic PGO in .NET, the runtime has much more visibility into the actual types being executed. The proposal is to teach the JIT to recognize specific "hot" LINQ chains.
For example, consider this pattern:
int sum = myArray.Where(x => x > 0).Sum();Instead of executing the standard Enumerable methods, the JIT could optimize this at runtime by:
- Pattern Matching: Recognizing the
.Where().Sum()chain on a genericT[]. - Inlining & Unrolling: Generating code equivalent to a manual loop:
// Concept of generated machine code int sum = 0; for (int i = 0; i < myArray.Length; i++) { if (myArray[i] > 0) sum += myArray[i]; }
- Vectorization: Applying SIMD optimizations to the generated loop.
Impact
This would be a massive "Quality of Life" improvement for the entire ecosystem:
- Zero-Cost Abstractions: Bringing C# LINQ performance closer to Rust Iterators or C++ Ranges.
- Performance: Immediate speedup for millions of existing lines of code without recompilation or refactoring.
- Simplification: Removal of "hand-optimized" loops in libraries, making the codebase more maintainable.
Feasibility
While extremely complex, the foundation is already laid out with Dynamic PGO and OSR (On-Stack Replacement). The JIT already performs Loop Cloning and GDV (Guarded Devirtualization). Extending this logic to recognize "Well-Known LINQ Methods" could be the next frontier in .NET optimization.