-
Notifications
You must be signed in to change notification settings - Fork 5.4k
Closed
Labels
area-CodeGen-coreclrCLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMICLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI
Milestone
Description
Description
Here is a benchmark that shows the behavior
[MemoryDiagnoser]
//[ShortRunJob(RuntimeMoniker.Net90)]
[ShortRunJob(RuntimeMoniker.Net10_0)]
[CategoriesColumn]
[HideColumns(Column.Job, Column.Error, Column.Median, Column.RatioSD, Column.Ratio, Column.AllocRatio, Column.StdDev, Column.Gen0)]
public class ForeachOverCollectionLiterals
{
// For IList the optimization works in both cases, since the actual type is List<T>
private readonly IList<int> List = [1, 2, 3, 4, 5];
// Iterating over a readonly interface will allocate for instance fields
// and won't allocate for static fields!
// It seems that the JIT compiler can't optimized a case when GetEnumerator delegates
// the work to a field.
// See GeneratedReadOnlyArray implementation.
private readonly IEnumerable<int> ReadOnlyList = [1, 2, 3, 4, 5];
private static readonly IEnumerable<int> StaticReadOnlyList = [1, 2, 3, 4, 5];
[Benchmark]
public int Foreach_Over_IList()
{
int result = 0;
foreach (var e in List)
{
result += e;
}
return result;
}
[Benchmark]
public int Foreach_Over_IReadOnlyList()
{
int result = 0;
foreach (var e in ReadOnlyList)
{
result += e;
}
return result;
}
[Benchmark]
public int Foreach_Over_IReadOnlyList_Static()
{
int result = 0;
foreach (var e in StaticReadOnlyList)
{
result += e;
}
return result;
}
}The output:
| Method | Mean | Allocated |
|---------------------------------- |----------:|----------:|
| Foreach_Over_IList | 3.411 ns | - |
| Foreach_Over_IReadOnlyList | 11.999 ns | 32 B |
| Foreach_Over_IReadOnlyList_Static | 1.192 ns | - |
The JIT definitely can de-vritualize calls on instance fields, if the actual type of the field is a concrete collection type, the de-virtualization works.
It seems that its something to do with how ReadOnlyArray is represented:
IEnumerator<T> IEnumerable<T>.GetEnumerator()
{
return ((IEnumerable<T>)_items).GetEnumerator();
}Reproduction Steps
See the example above.
Expected behavior
The behavior should be the same for instance and static fields.
Actual behavior
The iterator is still allocated on the heap.
Regression?
No.
Known Workarounds
No response
Configuration
No response
Other information
No response
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
area-CodeGen-coreclrCLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMICLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI