Without JIT support, using Linq involves at least allocating an IEnumerator object on the heap, and a closure object, and a delegate to it (if said delegate captures local vars). Each call to `Select` or `Where` is also a virtual call.
This is hugely expensive compared to just a for loop. With this update it seems like the JIT can do escape analysis to stack-allocate the closure object, and the delegate as well (it could devirtualize calls even before that). It seems like it has everything to optimize away the whole LINQ overhead, though I'm not sure what happens in practice.
It'd be neat since that was a major argument against actually using LINQ in perf-sensitive code.
Sometimes, yes.
Linq contains a goodly number of hand-crafted special-case enumerators for common collections, or collections with certain interfaces, or span projections that are really nice optimizations but can complicate things for the JIT.
Some details here if you're curious: https://github.com/dotnet/runtime/blob/main/docs/design/core...
There's also ZLinq: https://github.com/Cysharp/ZLinq