-
Notifications
You must be signed in to change notification settings - Fork 4.2k
Open
Labels
Area-CompilersCode Gen QualityRoom for improvement in the quality of the compiler's generated codeRoom for improvement in the quality of the compiler's generated codeFeature - Collection ExpressionsuntriagedIssues and PRs which have not yet been triaged by a leadIssues and PRs which have not yet been triaged by a lead
Milestone
Description
Targeting .NET 10
. Consider the sample below. (Might be related to #78106.)
In a simple switch expression, I return an IEnumerable<char>
for a bunch of known values. For unknown values, I want to fulfill the contract of the enumerable, so I use the nice collection expression syntax to return [c]
. Looks nice, but on a very hot path, I noticed significant GC.
Question
Turns out that the collection expression allocates a temporary array, apparently for no reason. Shouldn't the optimizer/jitter be able to optimize away this allocation?
Sample project
Obviously a lot simplified compared to my real world example.
namespace SingleElementCollectionAllocation;
internal class Program
{
static void Main(string[] args)
{
Console.WriteLine(GetCharacters("WHATEVER"));
}
private static IEnumerable<char> GetCharacters(string s)
{
return s.SelectMany(Unwanted_Collection_Allocation);
}
private static IEnumerable<char> Unwanted_Collection_Allocation(char c)
{
return c switch
{
'X' => DoubleUp(c),
// Unwanted allocation here:
// IL_000f: newobj instance void class '<>z__ReadOnlySingleElementList`1'<char>::.ctor(!0)
_ => [c]
};
}
private static IEnumerable<char> Ugly_Syntax_But_No_Collection_Allocation(char c)
{
return c switch
{
'X' => DoubleUp(c),
_ => SingleElementHelper(c)
};
}
private static IEnumerable<char> SingleElementHelper(char c)
{
yield return c;
}
private static IEnumerable<char> DoubleUp(char c)
{
yield return c;
yield return c;
}
}
Generated IL code
.method private hidebysig static class [System.Runtime]System.Collections.Generic.IEnumerable`1<char>
Unwanted_Collection_Allocation(char c) cil managed
{
// Code size 23 (0x17)
.maxstack 2
.locals init (class [System.Runtime]System.Collections.Generic.IEnumerable`1<char> V_0)
IL_0000: ldarg.0
IL_0001: ldc.i4.s 88
IL_0003: bne.un.s IL_000e
IL_0005: ldarg.0
IL_0006: call class [System.Runtime]System.Collections.Generic.IEnumerable`1<char> SingleElementCollectionAllocation.Program::DoubleUp(char)
IL_000b: stloc.0
IL_000c: br.s IL_0015
IL_000e: ldarg.0
IL_000f: newobj instance void class '<>z__ReadOnlySingleElementList`1'<char>::.ctor(!0)
IL_0014: stloc.0
IL_0015: ldloc.0
IL_0016: ret
} // end of method Program::Unwanted_Collection_Allocation
Enderlook
Metadata
Metadata
Assignees
Labels
Area-CompilersCode Gen QualityRoom for improvement in the quality of the compiler's generated codeRoom for improvement in the quality of the compiler's generated codeFeature - Collection ExpressionsuntriagedIssues and PRs which have not yet been triaged by a leadIssues and PRs which have not yet been triaged by a lead