Various optimization opportunities
Opened this issue · 1 comments
This is a list of various optimization opportunities I have spotted so far. I'd like to keep it here rather than in the notes that are bound to be eventually lost just in case you may want to consider implementing any of those yourself.
- Detect struct/non-interface-based
Enumerator GetEnumerator()
implementation on types and target it and its return type overIEnumerator<T> GetEnumerator()
- Use
CollectionsMarshal.AsSpan()
andCollectionsMarshal.SetCount()
to presize enumerable materialization into lists of potentially known length (where assumed length <= presized length) - Use array builder like type when materializing enumerables to array (e.g. @neuecc 's https://github.com/dotnet/runtime/pull/90459/files#diff-13dbacecc657e4cd1f8e4e1474b1e573c2cf749d36b9e918b586e7d6df9ddbd4R148 optimized for elementwise inserts or https://github.com/U8String/U8String/blob/main/src/Helpers/ArrayBuilder.cs for range inserts where the memory has to be contiguous)
- Consider using either
stackalloc
orInlineArray
for initial buffer (with caveats around large structs and managed T) - Consider pooling intermediate arrays (with subsequent Return(arr, clearArray: true) to prevent potentially sensitive data leaking to ArrayPool)
- Consider using either
- Emit throw helpers over
throw
to avoid codegen regressions caused by the presence of EH
I forgot to reply to this the other day, but I think the main challenge in using an array builder would be in how to embed the implementation on the output module. The easiest approach would probably be to add the source on MSBuild task project, and let trimming or whatever deal with them if they're left unused.
The struct enumerator thing could be more easily done when expanding LINQ queries based around enumerator sources, but there are some other stages like SelectMany() that always fallback to the interfaces, so maybe a more generic approach would be beneficial.