dubiousconst282/DistIL

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 over IEnumerator<T> GetEnumerator()
  • Use CollectionsMarshal.AsSpan() and CollectionsMarshal.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 or InlineArray 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)
  • 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.