Special case GetEnumerator for empty collections in .NET 8 v .NET 7 benchmarks




Date Added (UTC):

15 Apr 2024 @ 21:38

Date Updated (UTC):

16 Apr 2024 @ 03:02


.NET Version(s):

.NET 7 .NET 8

Tag(s):

#.Net8PerfImprovement #Collections


Added By:
Profile Image

Blog   
Ireland    
.NET Developer and tech lead from Ireland!

Benchmark Results:





Benchmark Code:



using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Columns;
using BenchmarkDotNet.Configs;
using BenchmarkDotNet.Environments;
using BenchmarkDotNet.Jobs;
using BenchmarkDotNet.Reports;
using System;
using System.Collections.Generic;

[HideColumns("Error", "StdDev", "Median", "RatioSD")]
[MemoryDiagnoser(displayGenColumns: false)]
[Config(typeof(Config))]
public class SpecialCaseEmptyCollections
{
    private readonly IEnumerable<int> _list = new List<int>();
    private readonly IEnumerable<int> _queue = new Queue<int>();
    private readonly IEnumerable<int> _stack = new Stack<int>();
    private readonly IEnumerable<int> _linkedList = new LinkedList<int>();
    private readonly IEnumerable<int> _hashSet = new HashSet<int>();
    private readonly IEnumerable<int> _segment = new ArraySegment<int>(Array.Empty<int>());
    private readonly IEnumerable<KeyValuePair<int, int>> _dictionary = new Dictionary<int, int>();
    private readonly IEnumerable<KeyValuePair<int, int>> _sortedDictionary = new SortedDictionary<int, int>();
    private readonly IEnumerable<KeyValuePair<int, int>> _sortedList = new SortedList<int, int>();
    private readonly IEnumerable<(int, int)> _priorityQueue = new PriorityQueue<int, int>().UnorderedItems;

    [Benchmark] public IEnumerator<int> GetList() => _list.GetEnumerator();
    [Benchmark] public IEnumerator<int> GetQueue() => _queue.GetEnumerator();
    [Benchmark] public IEnumerator<int> GetStack() => _stack.GetEnumerator();
    [Benchmark] public IEnumerator<int> GetLinkedList() => _linkedList.GetEnumerator();
    [Benchmark] public IEnumerator<int> GetHashSet() => _hashSet.GetEnumerator();
    [Benchmark] public IEnumerator<int> GetArraySegment() => _segment.GetEnumerator();
    [Benchmark] public IEnumerator<KeyValuePair<int, int>> GetDictionary() => _dictionary.GetEnumerator();
    [Benchmark] public IEnumerator<KeyValuePair<int, int>> GetSortedDictionary() => _sortedDictionary.GetEnumerator();
    [Benchmark] public IEnumerator<KeyValuePair<int, int>> GetSortedList() => _sortedList.GetEnumerator();
    [Benchmark] public IEnumerator<(int, int)> GetPriorityQueue() => _priorityQueue.GetEnumerator();

    private class Config : ManualConfig
    {
        public Config()
        {
            AddJob(Job.Default.WithId(".NET 7").WithRuntime(CoreRuntime.Core70).AsBaseline());
            AddJob(Job.Default.WithId(".NET 8").WithRuntime(CoreRuntime.Core80));

            SummaryStyle =
                SummaryStyle.Default.WithRatioStyle(RatioStyle.Percentage);
        }
    }
}

// .NET 7, .NET 8
public IEnumerator<int> GetList()
{
    return _list.GetEnumerator();
}
// .NET 7, .NET 8
public IEnumerator<int> GetQueue()
{
    return _queue.GetEnumerator();
}
// .NET 7, .NET 8
public IEnumerator<int> GetStack()
{
    return _stack.GetEnumerator();
}
// .NET 7, .NET 8
public IEnumerator<int> GetLinkedList()
{
    return _linkedList.GetEnumerator();
}
// .NET 7, .NET 8
public IEnumerator<int> GetHashSet()
{
    return _hashSet.GetEnumerator();
}
// .NET 7, .NET 8
public IEnumerator<int> GetArraySegment()
{
    return _segment.GetEnumerator();
}
// .NET 7, .NET 8
public IEnumerator<KeyValuePair<int, int>> GetDictionary()
{
    return _dictionary.GetEnumerator();
}
// .NET 7, .NET 8
public IEnumerator<KeyValuePair<int, int>> GetSortedDictionary()
{
    return _sortedDictionary.GetEnumerator();
}
// .NET 7, .NET 8
public IEnumerator<KeyValuePair<int, int>> GetSortedList()
{
    return _sortedList.GetEnumerator();
}
// .NET 7, .NET 8
public IEnumerator<ValueTuple<int, int>> GetPriorityQueue()
{
    return _priorityQueue.GetEnumerator();
}

// .NET 7
.method public hidebysig 
    instance class [System.Runtime]System.Collections.Generic.IEnumerator`1<int32> GetList () cil managed 
{
    .custom instance void [BenchmarkDotNet.Annotations]BenchmarkDotNet.Attributes.BenchmarkAttribute::.ctor(int32, string) = (
        01 00 25 00 00 00 01 5f 00 00
    )
    // Method begins at RVA 0x209d
    // Code size 12 (0xc)
    .maxstack 8

    // sequence point: (line 37, col 54) to (line 37, col 75) in _
    IL_0000: ldarg.0
    IL_0001: ldfld class [System.Runtime]System.Collections.Generic.IEnumerable`1<int32> SpecialCaseEmptyCollections::_list
    IL_0006: callvirt instance class [System.Runtime]System.Collections.Generic.IEnumerator`1<!0> class [System.Runtime]System.Collections.Generic.IEnumerable`1<int32>::GetEnumerator()
    IL_000b: ret
}

// .NET 8
.method public hidebysig 
    instance class [System.Runtime]System.Collections.Generic.IEnumerator`1<int32> GetList () cil managed 
{
    .custom instance void [BenchmarkDotNet.Annotations]BenchmarkDotNet.Attributes.BenchmarkAttribute::.ctor(int32, string) = (
        01 00 25 00 00 00 01 5f 00 00
    )
    // Method begins at RVA 0x2050
    // Code size 12 (0xc)
    .maxstack 8

    // sequence point: (line 37, col 54) to (line 37, col 75) in _
    IL_0000: ldarg.0
    IL_0001: ldfld class [System.Runtime]System.Collections.Generic.IEnumerable`1<int32> SpecialCaseEmptyCollections::_list
    IL_0006: callvirt instance class [System.Runtime]System.Collections.Generic.IEnumerator`1<!0> class [System.Runtime]System.Collections.Generic.IEnumerable`1<int32>::GetEnumerator()
    IL_000b: ret
}
// .NET 7
.method public hidebysig 
    instance class [System.Runtime]System.Collections.Generic.IEnumerator`1<int32> GetQueue () cil managed 
{
    .custom instance void [BenchmarkDotNet.Annotations]BenchmarkDotNet.Attributes.BenchmarkAttribute::.ctor(int32, string) = (
        01 00 26 00 00 00 01 5f 00 00
    )
    // Method begins at RVA 0x20aa
    // Code size 12 (0xc)
    .maxstack 8

    // sequence point: (line 38, col 55) to (line 38, col 77) in _
    IL_0000: ldarg.0
    IL_0001: ldfld class [System.Runtime]System.Collections.Generic.IEnumerable`1<int32> SpecialCaseEmptyCollections::_queue
    IL_0006: callvirt instance class [System.Runtime]System.Collections.Generic.IEnumerator`1<!0> class [System.Runtime]System.Collections.Generic.IEnumerable`1<int32>::GetEnumerator()
    IL_000b: ret
}

// .NET 8
.method public hidebysig 
    instance class [System.Runtime]System.Collections.Generic.IEnumerator`1<int32> GetQueue () cil managed 
{
    .custom instance void [BenchmarkDotNet.Annotations]BenchmarkDotNet.Attributes.BenchmarkAttribute::.ctor(int32, string) = (
        01 00 26 00 00 00 01 5f 00 00
    )
    // Method begins at RVA 0x205d
    // Code size 12 (0xc)
    .maxstack 8

    // sequence point: (line 38, col 55) to (line 38, col 77) in _
    IL_0000: ldarg.0
    IL_0001: ldfld class [System.Runtime]System.Collections.Generic.IEnumerable`1<int32> SpecialCaseEmptyCollections::_queue
    IL_0006: callvirt instance class [System.Runtime]System.Collections.Generic.IEnumerator`1<!0> class [System.Runtime]System.Collections.Generic.IEnumerable`1<int32>::GetEnumerator()
    IL_000b: ret
}
// .NET 7
.method public hidebysig 
    instance class [System.Runtime]System.Collections.Generic.IEnumerator`1<int32> GetStack () cil managed 
{
    .custom instance void [BenchmarkDotNet.Annotations]BenchmarkDotNet.Attributes.BenchmarkAttribute::.ctor(int32, string) = (
        01 00 27 00 00 00 01 5f 00 00
    )
    // Method begins at RVA 0x20b7
    // Code size 12 (0xc)
    .maxstack 8

    // sequence point: (line 39, col 55) to (line 39, col 77) in _
    IL_0000: ldarg.0
    IL_0001: ldfld class [System.Runtime]System.Collections.Generic.IEnumerable`1<int32> SpecialCaseEmptyCollections::_stack
    IL_0006: callvirt instance class [System.Runtime]System.Collections.Generic.IEnumerator`1<!0> class [System.Runtime]System.Collections.Generic.IEnumerable`1<int32>::GetEnumerator()
    IL_000b: ret
}

// .NET 8
.method public hidebysig 
    instance class [System.Runtime]System.Collections.Generic.IEnumerator`1<int32> GetStack () cil managed 
{
    .custom instance void [BenchmarkDotNet.Annotations]BenchmarkDotNet.Attributes.BenchmarkAttribute::.ctor(int32, string) = (
        01 00 27 00 00 00 01 5f 00 00
    )
    // Method begins at RVA 0x206a
    // Code size 12 (0xc)
    .maxstack 8

    // sequence point: (line 39, col 55) to (line 39, col 77) in _
    IL_0000: ldarg.0
    IL_0001: ldfld class [System.Runtime]System.Collections.Generic.IEnumerable`1<int32> SpecialCaseEmptyCollections::_stack
    IL_0006: callvirt instance class [System.Runtime]System.Collections.Generic.IEnumerator`1<!0> class [System.Runtime]System.Collections.Generic.IEnumerable`1<int32>::GetEnumerator()
    IL_000b: ret
}
// .NET 7
.method public hidebysig 
    instance class [System.Runtime]System.Collections.Generic.IEnumerator`1<int32> GetLinkedList () cil managed 
{
    .custom instance void [BenchmarkDotNet.Annotations]BenchmarkDotNet.Attributes.BenchmarkAttribute::.ctor(int32, string) = (
        01 00 28 00 00 00 01 5f 00 00
    )
    // Method begins at RVA 0x20c4
    // Code size 12 (0xc)
    .maxstack 8

    // sequence point: (line 40, col 60) to (line 40, col 87) in _
    IL_0000: ldarg.0
    IL_0001: ldfld class [System.Runtime]System.Collections.Generic.IEnumerable`1<int32> SpecialCaseEmptyCollections::_linkedList
    IL_0006: callvirt instance class [System.Runtime]System.Collections.Generic.IEnumerator`1<!0> class [System.Runtime]System.Collections.Generic.IEnumerable`1<int32>::GetEnumerator()
    IL_000b: ret
}

// .NET 8
.method public hidebysig 
    instance class [System.Runtime]System.Collections.Generic.IEnumerator`1<int32> GetLinkedList () cil managed 
{
    .custom instance void [BenchmarkDotNet.Annotations]BenchmarkDotNet.Attributes.BenchmarkAttribute::.ctor(int32, string) = (
        01 00 28 00 00 00 01 5f 00 00
    )
    // Method begins at RVA 0x2077
    // Code size 12 (0xc)
    .maxstack 8

    // sequence point: (line 40, col 60) to (line 40, col 87) in _
    IL_0000: ldarg.0
    IL_0001: ldfld class [System.Runtime]System.Collections.Generic.IEnumerable`1<int32> SpecialCaseEmptyCollections::_linkedList
    IL_0006: callvirt instance class [System.Runtime]System.Collections.Generic.IEnumerator`1<!0> class [System.Runtime]System.Collections.Generic.IEnumerable`1<int32>::GetEnumerator()
    IL_000b: ret
}
// .NET 7
.method public hidebysig 
    instance class [System.Runtime]System.Collections.Generic.IEnumerator`1<int32> GetHashSet () cil managed 
{
    .custom instance void [BenchmarkDotNet.Annotations]BenchmarkDotNet.Attributes.BenchmarkAttribute::.ctor(int32, string) = (
        01 00 29 00 00 00 01 5f 00 00
    )
    // Method begins at RVA 0x20d1
    // Code size 12 (0xc)
    .maxstack 8

    // sequence point: (line 41, col 57) to (line 41, col 81) in _
    IL_0000: ldarg.0
    IL_0001: ldfld class [System.Runtime]System.Collections.Generic.IEnumerable`1<int32> SpecialCaseEmptyCollections::_hashSet
    IL_0006: callvirt instance class [System.Runtime]System.Collections.Generic.IEnumerator`1<!0> class [System.Runtime]System.Collections.Generic.IEnumerable`1<int32>::GetEnumerator()
    IL_000b: ret
}

// .NET 8
.method public hidebysig 
    instance class [System.Runtime]System.Collections.Generic.IEnumerator`1<int32> GetHashSet () cil managed 
{
    .custom instance void [BenchmarkDotNet.Annotations]BenchmarkDotNet.Attributes.BenchmarkAttribute::.ctor(int32, string) = (
        01 00 29 00 00 00 01 5f 00 00
    )
    // Method begins at RVA 0x2084
    // Code size 12 (0xc)
    .maxstack 8

    // sequence point: (line 41, col 57) to (line 41, col 81) in _
    IL_0000: ldarg.0
    IL_0001: ldfld class [System.Runtime]System.Collections.Generic.IEnumerable`1<int32> SpecialCaseEmptyCollections::_hashSet
    IL_0006: callvirt instance class [System.Runtime]System.Collections.Generic.IEnumerator`1<!0> class [System.Runtime]System.Collections.Generic.IEnumerable`1<int32>::GetEnumerator()
    IL_000b: ret
}
// .NET 7
.method public hidebysig 
    instance class [System.Runtime]System.Collections.Generic.IEnumerator`1<int32> GetArraySegment () cil managed 
{
    .custom instance void [BenchmarkDotNet.Annotations]BenchmarkDotNet.Attributes.BenchmarkAttribute::.ctor(int32, string) = (
        01 00 2a 00 00 00 01 5f 00 00
    )
    // Method begins at RVA 0x20de
    // Code size 12 (0xc)
    .maxstack 8

    // sequence point: (line 42, col 62) to (line 42, col 86) in _
    IL_0000: ldarg.0
    IL_0001: ldfld class [System.Runtime]System.Collections.Generic.IEnumerable`1<int32> SpecialCaseEmptyCollections::_segment
    IL_0006: callvirt instance class [System.Runtime]System.Collections.Generic.IEnumerator`1<!0> class [System.Runtime]System.Collections.Generic.IEnumerable`1<int32>::GetEnumerator()
    IL_000b: ret
}

// .NET 8
.method public hidebysig 
    instance class [System.Runtime]System.Collections.Generic.IEnumerator`1<int32> GetArraySegment () cil managed 
{
    .custom instance void [BenchmarkDotNet.Annotations]BenchmarkDotNet.Attributes.BenchmarkAttribute::.ctor(int32, string) = (
        01 00 2a 00 00 00 01 5f 00 00
    )
    // Method begins at RVA 0x2091
    // Code size 12 (0xc)
    .maxstack 8

    // sequence point: (line 42, col 62) to (line 42, col 86) in _
    IL_0000: ldarg.0
    IL_0001: ldfld class [System.Runtime]System.Collections.Generic.IEnumerable`1<int32> SpecialCaseEmptyCollections::_segment
    IL_0006: callvirt instance class [System.Runtime]System.Collections.Generic.IEnumerator`1<!0> class [System.Runtime]System.Collections.Generic.IEnumerable`1<int32>::GetEnumerator()
    IL_000b: ret
}
// .NET 7
.method public hidebysig 
    instance class [System.Runtime]System.Collections.Generic.IEnumerator`1<valuetype [System.Runtime]System.Collections.Generic.KeyValuePair`2<int32, int32>> GetDictionary () cil managed 
{
    .custom instance void [BenchmarkDotNet.Annotations]BenchmarkDotNet.Attributes.BenchmarkAttribute::.ctor(int32, string) = (
        01 00 2b 00 00 00 01 5f 00 00
    )
    .param [0]
        .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8[]) = (
            01 00 02 00 00 00 01 00 00 00
        )
    // Method begins at RVA 0x20eb
    // Code size 12 (0xc)
    .maxstack 8

    // sequence point: (line 43, col 79) to (line 43, col 106) in _
    IL_0000: ldarg.0
    IL_0001: ldfld class [System.Runtime]System.Collections.Generic.IEnumerable`1<valuetype [System.Runtime]System.Collections.Generic.KeyValuePair`2<int32, int32>> SpecialCaseEmptyCollections::_dictionary
    IL_0006: callvirt instance class [System.Runtime]System.Collections.Generic.IEnumerator`1<!0> class [System.Runtime]System.Collections.Generic.IEnumerable`1<valuetype [System.Runtime]System.Collections.Generic.KeyValuePair`2<int32, int32>>::GetEnumerator()
    IL_000b: ret
}

// .NET 8
.method public hidebysig 
    instance class [System.Runtime]System.Collections.Generic.IEnumerator`1<valuetype [System.Runtime]System.Collections.Generic.KeyValuePair`2<int32, int32>> GetDictionary () cil managed 
{
    .custom instance void [BenchmarkDotNet.Annotations]BenchmarkDotNet.Attributes.BenchmarkAttribute::.ctor(int32, string) = (
        01 00 2b 00 00 00 01 5f 00 00
    )
    .param [0]
        .custom instance void [System.Runtime]System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8[]) = (
            01 00 02 00 00 00 01 00 00 00
        )
    // Method begins at RVA 0x209e
    // Code size 12 (0xc)
    .maxstack 8

    // sequence point: (line 43, col 79) to (line 43, col 106) in _
    IL_0000: ldarg.0
    IL_0001: ldfld class [System.Runtime]System.Collections.Generic.IEnumerable`1<valuetype [System.Runtime]System.Collections.Generic.KeyValuePair`2<int32, int32>> SpecialCaseEmptyCollections::_dictionary
    IL_0006: callvirt instance class [System.Runtime]System.Collections.Generic.IEnumerator`1<!0> class [System.Runtime]System.Collections.Generic.IEnumerable`1<valuetype [System.Runtime]System.Collections.Generic.KeyValuePair`2<int32, int32>>::GetEnumerator()
    IL_000b: ret
}
// .NET 7
.method public hidebysig 
    instance class [System.Runtime]System.Collections.Generic.IEnumerator`1<valuetype [System.Runtime]System.Collections.Generic.KeyValuePair`2<int32, int32>> GetSortedDictionary () cil managed 
{
    .custom instance void [BenchmarkDotNet.Annotations]BenchmarkDotNet.Attributes.BenchmarkAttribute::.ctor(int32, string) = (
        01 00 2c 00 00 00 01 5f 00 00
    )
    .param [0]
        .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8[]) = (
            01 00 02 00 00 00 01 00 00 00
        )
    // Method begins at RVA 0x20f8
    // Code size 12 (0xc)
    .maxstack 8

    // sequence point: (line 44, col 85) to (line 44, col 118) in _
    IL_0000: ldarg.0
    IL_0001: ldfld class [System.Runtime]System.Collections.Generic.IEnumerable`1<valuetype [System.Runtime]System.Collections.Generic.KeyValuePair`2<int32, int32>> SpecialCaseEmptyCollections::_sortedDictionary
    IL_0006: callvirt instance class [System.Runtime]System.Collections.Generic.IEnumerator`1<!0> class [System.Runtime]System.Collections.Generic.IEnumerable`1<valuetype [System.Runtime]System.Collections.Generic.KeyValuePair`2<int32, int32>>::GetEnumerator()
    IL_000b: ret
}

// .NET 8
.method public hidebysig 
    instance class [System.Runtime]System.Collections.Generic.IEnumerator`1<valuetype [System.Runtime]System.Collections.Generic.KeyValuePair`2<int32, int32>> GetSortedDictionary () cil managed 
{
    .custom instance void [BenchmarkDotNet.Annotations]BenchmarkDotNet.Attributes.BenchmarkAttribute::.ctor(int32, string) = (
        01 00 2c 00 00 00 01 5f 00 00
    )
    .param [0]
        .custom instance void [System.Runtime]System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8[]) = (
            01 00 02 00 00 00 01 00 00 00
        )
    // Method begins at RVA 0x20ab
    // Code size 12 (0xc)
    .maxstack 8

    // sequence point: (line 44, col 85) to (line 44, col 118) in _
    IL_0000: ldarg.0
    IL_0001: ldfld class [System.Runtime]System.Collections.Generic.IEnumerable`1<valuetype [System.Runtime]System.Collections.Generic.KeyValuePair`2<int32, int32>> SpecialCaseEmptyCollections::_sortedDictionary
    IL_0006: callvirt instance class [System.Runtime]System.Collections.Generic.IEnumerator`1<!0> class [System.Runtime]System.Collections.Generic.IEnumerable`1<valuetype [System.Runtime]System.Collections.Generic.KeyValuePair`2<int32, int32>>::GetEnumerator()
    IL_000b: ret
}
// .NET 7
.method public hidebysig 
    instance class [System.Runtime]System.Collections.Generic.IEnumerator`1<valuetype [System.Runtime]System.Collections.Generic.KeyValuePair`2<int32, int32>> GetSortedList () cil managed 
{
    .custom instance void [BenchmarkDotNet.Annotations]BenchmarkDotNet.Attributes.BenchmarkAttribute::.ctor(int32, string) = (
        01 00 2d 00 00 00 01 5f 00 00
    )
    .param [0]
        .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8[]) = (
            01 00 02 00 00 00 01 00 00 00
        )
    // Method begins at RVA 0x2105
    // Code size 12 (0xc)
    .maxstack 8

    // sequence point: (line 45, col 79) to (line 45, col 106) in _
    IL_0000: ldarg.0
    IL_0001: ldfld class [System.Runtime]System.Collections.Generic.IEnumerable`1<valuetype [System.Runtime]System.Collections.Generic.KeyValuePair`2<int32, int32>> SpecialCaseEmptyCollections::_sortedList
    IL_0006: callvirt instance class [System.Runtime]System.Collections.Generic.IEnumerator`1<!0> class [System.Runtime]System.Collections.Generic.IEnumerable`1<valuetype [System.Runtime]System.Collections.Generic.KeyValuePair`2<int32, int32>>::GetEnumerator()
    IL_000b: ret
}

// .NET 8
.method public hidebysig 
    instance class [System.Runtime]System.Collections.Generic.IEnumerator`1<valuetype [System.Runtime]System.Collections.Generic.KeyValuePair`2<int32, int32>> GetSortedList () cil managed 
{
    .custom instance void [BenchmarkDotNet.Annotations]BenchmarkDotNet.Attributes.BenchmarkAttribute::.ctor(int32, string) = (
        01 00 2d 00 00 00 01 5f 00 00
    )
    .param [0]
        .custom instance void [System.Runtime]System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8[]) = (
            01 00 02 00 00 00 01 00 00 00
        )
    // Method begins at RVA 0x20b8
    // Code size 12 (0xc)
    .maxstack 8

    // sequence point: (line 45, col 79) to (line 45, col 106) in _
    IL_0000: ldarg.0
    IL_0001: ldfld class [System.Runtime]System.Collections.Generic.IEnumerable`1<valuetype [System.Runtime]System.Collections.Generic.KeyValuePair`2<int32, int32>> SpecialCaseEmptyCollections::_sortedList
    IL_0006: callvirt instance class [System.Runtime]System.Collections.Generic.IEnumerator`1<!0> class [System.Runtime]System.Collections.Generic.IEnumerable`1<valuetype [System.Runtime]System.Collections.Generic.KeyValuePair`2<int32, int32>>::GetEnumerator()
    IL_000b: ret
}
// .NET 7
.method public hidebysig 
    instance class [System.Runtime]System.Collections.Generic.IEnumerator`1<valuetype [System.Runtime]System.ValueTuple`2<int32, int32>> GetPriorityQueue () cil managed 
{
    .custom instance void [BenchmarkDotNet.Annotations]BenchmarkDotNet.Attributes.BenchmarkAttribute::.ctor(int32, string) = (
        01 00 2e 00 00 00 01 5f 00 00
    )
    .param [0]
        .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8[]) = (
            01 00 02 00 00 00 01 00 00 00
        )
    // Method begins at RVA 0x2112
    // Code size 12 (0xc)
    .maxstack 8

    // sequence point: (line 46, col 70) to (line 46, col 100) in _
    IL_0000: ldarg.0
    IL_0001: ldfld class [System.Runtime]System.Collections.Generic.IEnumerable`1<valuetype [System.Runtime]System.ValueTuple`2<int32, int32>> SpecialCaseEmptyCollections::_priorityQueue
    IL_0006: callvirt instance class [System.Runtime]System.Collections.Generic.IEnumerator`1<!0> class [System.Runtime]System.Collections.Generic.IEnumerable`1<valuetype [System.Runtime]System.ValueTuple`2<int32, int32>>::GetEnumerator()
    IL_000b: ret
}

// .NET 8
.method public hidebysig 
    instance class [System.Runtime]System.Collections.Generic.IEnumerator`1<valuetype [System.Runtime]System.ValueTuple`2<int32, int32>> GetPriorityQueue () cil managed 
{
    .custom instance void [BenchmarkDotNet.Annotations]BenchmarkDotNet.Attributes.BenchmarkAttribute::.ctor(int32, string) = (
        01 00 2e 00 00 00 01 5f 00 00
    )
    .param [0]
        .custom instance void [System.Runtime]System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8[]) = (
            01 00 02 00 00 00 01 00 00 00
        )
    // Method begins at RVA 0x20c5
    // Code size 12 (0xc)
    .maxstack 8

    // sequence point: (line 46, col 70) to (line 46, col 100) in _
    IL_0000: ldarg.0
    IL_0001: ldfld class [System.Runtime]System.Collections.Generic.IEnumerable`1<valuetype [System.Runtime]System.ValueTuple`2<int32, int32>> SpecialCaseEmptyCollections::_priorityQueue
    IL_0006: callvirt instance class [System.Runtime]System.Collections.Generic.IEnumerator`1<!0> class [System.Runtime]System.Collections.Generic.IEnumerable`1<valuetype [System.Runtime]System.ValueTuple`2<int32, int32>>::GetEnumerator()
    IL_000b: ret
}

// .NET 7 (X64)
GetList()
    L0000: mov rcx, [rcx+8]
    L0004: mov r11, 0x7ffd83c67000
    L000e: cmp [rcx], ecx
    L0010: jmp qword ptr [r11]
// .NET 8 (X64)
GetList()
    L0000: mov rcx, [rcx+8]
    L0004: mov r11, 0x7fff322da000
    L000e: cmp [rcx], ecx
    L0010: jmp qword ptr [r11]
// .NET 7 (X64)
GetQueue()
    L0000: mov rcx, [rcx+0x10]
    L0004: mov r11, 0x7ffd83c67008
    L000e: cmp [rcx], ecx
    L0010: jmp qword ptr [r11]
// .NET 8 (X64)
GetQueue()
    L0000: mov rcx, [rcx+0x10]
    L0004: mov r11, 0x7fff322da008
    L000e: cmp [rcx], ecx
    L0010: jmp qword ptr [r11]
// .NET 7 (X64)
GetStack()
    L0000: mov rcx, [rcx+0x18]
    L0004: mov r11, 0x7ffd83c67010
    L000e: cmp [rcx], ecx
    L0010: jmp qword ptr [r11]
// .NET 8 (X64)
GetStack()
    L0000: mov rcx, [rcx+0x18]
    L0004: mov r11, 0x7fff322da010
    L000e: cmp [rcx], ecx
    L0010: jmp qword ptr [r11]
// .NET 7 (X64)
GetLinkedList()
    L0000: mov rcx, [rcx+0x20]
    L0004: mov r11, 0x7ffd83c67018
    L000e: cmp [rcx], ecx
    L0010: jmp qword ptr [r11]
// .NET 8 (X64)
GetLinkedList()
    L0000: mov rcx, [rcx+0x20]
    L0004: mov r11, 0x7fff322da018
    L000e: cmp [rcx], ecx
    L0010: jmp qword ptr [r11]
// .NET 7 (X64)
GetHashSet()
    L0000: mov rcx, [rcx+0x28]
    L0004: mov r11, 0x7ffd83c67020
    L000e: cmp [rcx], ecx
    L0010: jmp qword ptr [r11]
// .NET 8 (X64)
GetHashSet()
    L0000: mov rcx, [rcx+0x28]
    L0004: mov r11, 0x7fff322da020
    L000e: cmp [rcx], ecx
    L0010: jmp qword ptr [r11]
// .NET 7 (X64)
GetArraySegment()
    L0000: mov rcx, [rcx+0x30]
    L0004: mov r11, 0x7ffd83c67028
    L000e: cmp [rcx], ecx
    L0010: jmp qword ptr [r11]
// .NET 8 (X64)
GetArraySegment()
    L0000: mov rcx, [rcx+0x30]
    L0004: mov r11, 0x7fff322da028
    L000e: cmp [rcx], ecx
    L0010: jmp qword ptr [r11]
// .NET 7 (X64)
GetDictionary()
    L0000: mov rcx, [rcx+0x38]
    L0004: mov r11, 0x7ffd83c67030
    L000e: cmp [rcx], ecx
    L0010: jmp qword ptr [r11]
// .NET 8 (X64)
GetDictionary()
    L0000: mov rcx, [rcx+0x38]
    L0004: mov r11, 0x7fff322da030
    L000e: cmp [rcx], ecx
    L0010: jmp qword ptr [r11]
// .NET 7 (X64)
GetSortedDictionary()
    L0000: mov rcx, [rcx+0x40]
    L0004: mov r11, 0x7ffd83c67038
    L000e: cmp [rcx], ecx
    L0010: jmp qword ptr [r11]
// .NET 8 (X64)
GetSortedDictionary()
    L0000: mov rcx, [rcx+0x40]
    L0004: mov r11, 0x7fff322da038
    L000e: cmp [rcx], ecx
    L0010: jmp qword ptr [r11]
// .NET 7 (X64)
GetSortedList()
    L0000: mov rcx, [rcx+0x48]
    L0004: mov r11, 0x7ffd83c67040
    L000e: cmp [rcx], ecx
    L0010: jmp qword ptr [r11]
// .NET 8 (X64)
GetSortedList()
    L0000: mov rcx, [rcx+0x48]
    L0004: mov r11, 0x7fff322da040
    L000e: cmp [rcx], ecx
    L0010: jmp qword ptr [r11]
// .NET 7 (X64)
GetPriorityQueue()
    L0000: mov rcx, [rcx+0x50]
    L0004: mov r11, 0x7ffd83c67048
    L000e: cmp [rcx], ecx
    L0010: jmp qword ptr [r11]
// .NET 8 (X64)
GetPriorityQueue()
    L0000: mov rcx, [rcx+0x50]
    L0004: mov r11, 0x7fff322da048
    L000e: cmp [rcx], ecx
    L0010: jmp qword ptr [r11]


Benchmark Description:


From Stephen Toub on the Performance Improvements in .NET 8 blog post ... _For example, dotnet/runtime#82499 special-cases “empty” on a bunch of the built-in collection types to return an empty singleton enumerator, thus avoiding allocating a largely useless object. This is wide-reaching, affecting List<T>, Queue<T>, Stack<T>, LinkedList<T>, PriorityQueue<TElement, TPriority>, SortedDictionary<TKey, TValue>, SortedList<TKey, TValue>, HashSet<T>, Dictionary<TKey, TValue>, and ArraySegment<T>. Interestingly, T[] was already on this plan (as were a few other collections, like ConditionalWeakTable<TKey, TValue>); if you called IEnumerable<T>.GetEnumerator on any T[] of length 0, you already got back a singleton enumerator hardcoded to return false from its MoveNext. That same enumerator singleton is what’s now returned from the GetEnumerator implementations of all of those cited collection types when they’re empty at the moment GetEnumerator is called._ [PR](https://github.com/dotnet/runtime/pull/82499)

The provided benchmark code is designed to measure the performance of obtaining an enumerator from various empty collection types in .NET. This includes common collection types such as `List<int>`, `Queue<int>`, `Stack<int>`, `LinkedList<int>`, `HashSet<int>`, `ArraySegment<int>`, as well as key-value pair collections like `Dictionary<int, int>`, `SortedDictionary<int, int>`, and `SortedList<int, int>`, and also a `PriorityQueue<int, int>`. The benchmark is set up using BenchmarkDotNet, a powerful .NET library for benchmarking, and is configured to compare the performance across two different .NET versions, specifically .NET 7 and .NET 8, to see if there are any performance improvements or regressions between these versions. ### General Setup - **.NET Versions:** The benchmark is explicitly configured to run against .NET 7 and .NET 8, allowing for a direct comparison of performance across these two versions of the .NET runtime. - **BenchmarkDotNet Configuration:** The benchmark class is annotated with attributes to control the output and behavior of the benchmark. `HideColumns` is used to simplify the output by hiding certain columns that are not critical for this comparison. `MemoryDiagnoser` is enabled with `displayGenColumns: false` to focus on memory allocations without showing detailed garbage collection generation data. - **Custom Config Class:** A custom configuration class `Config` is defined to specify the jobs for .NET 7 and .NET 8, setting .NET 7 as the baseline for comparison. The summary style is adjusted to use percentage ratios to easily see performance differences. ### Benchmark Methods Each benchmark method is designed to test the performance of obtaining an enumerator from an empty instance of a specific collection type. Enumerators are fundamental for iterating over collections, and the efficiency of obtaining them can impact the overall performance of applications, especially in scenarios where collections are frequently iterated over in their empty state. 1. **GetList, GetQueue, GetStack, etc.:** Each of these methods tests the performance of calling `GetEnumerator()` on an empty collection of a specific type. The rationale behind testing different collection types is that each collection has its own internal implementation and optimizations, which can lead to different performance characteristics when obtaining an enumerator. ### Importance and Expected Insights - **Performance Insights:** By benchmarking these methods, developers can gain insights into the overhead associated with obtaining enumerators from different types of collections when they are empty. This is particularly relevant for performance-critical applications where collections are frequently created, iterated over, and discarded. - **.NET Runtime Comparisons:** Comparing the results across .NET 7 and .NET 8 will highlight any performance optimizations or regressions introduced in the newer version of the .NET runtime. This can help developers make informed decisions about upgrading their applications to newer .NET versions. - **Optimization Opportunities:** Understanding which collections have lower overhead for obtaining enumerators can guide developers in choosing the most appropriate collection types for their applications, especially in scenarios where collections are often empty. ### Conclusion This benchmark setup is a targeted approach to understanding a specific aspect of collection performance in .NET. The insights gained from running these benchmarks can be valuable for optimizing application performance and for making informed decisions about which .NET runtime version to target.


Benchmark Comments: