Were y'all able to determine (and do you remember) what the cause of the inefficiencies was? Was it just because the technology is so new that optimizations don't really exist for it, or was it a more fundamental problem with LINQ?
Well, generally speaking things that have to create iterators or create anonymous methods (or really even reference virtual function tables)... it's just not as fast as array access. By far. And if the critical path of the application involves looping over this particular collection and every instruction added to that inner loop body is a significant performance cost...
In obviously-not-performance-critical stuff I've been known to use delegates in a car-like fashion instead of my earlier example's approach.
Also, when we switched from .NET/SlimDX to Unity, which uses Mono, I think we lost a lot of .NET's optimizations. For one the garbage collector was causing massive frame skip all of a sudden, which was easily traced to our use of "foreach" instead of "for". Once we removed the iterator-creating-and-discarding foreach and relied on for, the vast majority of our "transient" heap allocation simply went away. I really prefer foreach for a lot of reasons, but I can't justify multiple-times-a-minute frame/music stutter just so I can have that (it was driving a number of people here
nuts).
So, in short:
- There are a lot of places where every instruction counts. We've learned by experience that we cannot count on the compiler/JIT/CLR/whatever to reduce even foreach into its logical equivalent in minimum-number-of-instructions, much less something like LINQ.
- Avoiding transient heap allocation is critical to avoiding garbage collection, and avoiding garbage collection is critical to the user experience.