3


A Comparison about all C# For loops




Date Added (UTC):

23 Apr 2024 @ 15:58

Date Updated (UTC):

23 Apr 2024 @ 16:37


.NET Version(s):

.NET 6 .NET 8 .NET 9

Tag(s):

#.Net8PerfImprovement #.Net9PerfImprovement


Added By:
Profile Image

Blog   
Leeds, United Kingdom    
Microsoft MVP | A Developer

Benchmark Results:





Benchmark Code:



using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;
using System.Runtime.InteropServices;

namespace ForLoopsComparison;

[MemoryDiagnoser(false)]
[SimpleJob(RuntimeMoniker.Net60)]
[SimpleJob(RuntimeMoniker.Net80)]
[SimpleJob(RuntimeMoniker.Net90)]
public class AllForLoopBenchmark
{
    [Params(100_000)]
    public int ItemCount { get; set; }

    public List<int> Items;

    [GlobalSetup]
    public void GlobalSetup()
    {
        Items = Enumerable.Range(0, ItemCount).ToList();
    }

    [Benchmark]
    public void For()
    {
        for (int i = 0; i < ItemCount; i++)
        {
            DoSomeThing(Items[i]);
        }
    }

    [Benchmark]
    public void ForEach()
    {
        foreach (var item in Items)
        {
            DoSomeThing(item);
        }
    }

    [Benchmark]
    public void ForEach_Linq()
    {
        Items.ForEach(DoSomeThing);
    }

    [Benchmark]
    public void ForEach_Parallel()
    {
        Parallel.ForEach(Items, DoSomeThing);
    }

    [Benchmark]
    public void ForEach_LinqParallel()
    {
        Items.AsParallel().ForAll(DoSomeThing);
    }

    [Benchmark]
    public void For_Span()
    {
        var span = CollectionsMarshal.AsSpan(Items);
        for (int i = 0; i < span.Length; i++)
        {
            DoSomeThing(span[i]);
        }
    }

    [Benchmark]
    public void ForEach_Span()
    {
        foreach (var item in CollectionsMarshal.AsSpan(Items))
        {
            DoSomeThing(item);
        }
    }

    private void DoSomeThing(int i)
    {
        _ = i;
    }
}

// .NET 6, .NET 8
public void For()
{
    int num = 0;
    while (num < ItemCount)
    {
        DoSomeThing(Items[num]);
        num++;
    }
}
// .NET 6, .NET 8
public void ForEach()
{
    List<int>.Enumerator enumerator = Items.GetEnumerator();
    try
    {
        while (enumerator.MoveNext())
        {
            int current = enumerator.Current;
            DoSomeThing(current);
        }
    }
    finally
    {
        ((IDisposable)enumerator).Dispose();
    }
}
// .NET 6, .NET 8
public void ForEach_Linq()
{
    Items.ForEach(new Action<int>(DoSomeThing));
}
// .NET 6, .NET 8
public void ForEach_Parallel()
{
    Parallel.ForEach((IEnumerable<int>)Items, new Action<int>(DoSomeThing));
}
// .NET 6, .NET 8
public void ForEach_LinqParallel()
{
    ParallelEnumerable.ForAll(ParallelEnumerable.AsParallel(Items), new Action<int>(DoSomeThing));
}
// .NET 6, .NET 8
public void For_Span()
{
    Span<int> span = CollectionsMarshal.AsSpan(Items);
    int num = 0;
    while (num < span.Length)
    {
        DoSomeThing(span[num]);
        num++;
    }
}
// .NET 6, .NET 8
public void ForEach_Span()
{
    Span<int> span = CollectionsMarshal.AsSpan(Items);
    int num = 0;
    while (num < span.Length)
    {
        int i = span[num];
        DoSomeThing(i);
        num++;
    }
}

// .NET 6
.method public hidebysig 
    instance void For () cil managed 
{
    .custom instance void [BenchmarkDotNet.Annotations]BenchmarkDotNet.Attributes.BenchmarkAttribute::.ctor(int32, string) = (
        01 00 23 00 00 00 01 5f 00 00
    )
    // Method begins at RVA 0x20ac
    // Code size 36 (0x24)
    .maxstack 3
    .locals init (
        [0] int32 i
    )

    // sequence point: (line 38, col 14) to (line 38, col 23) in _
    IL_0000: ldc.i4.0
    IL_0001: stloc.0
    // sequence point: hidden
    IL_0002: br.s IL_001a
    // loop start (head: IL_001a)
        // sequence point: (line 40, col 13) to (line 40, col 35) in _
        IL_0004: ldarg.0
        IL_0005: ldarg.0
        IL_0006: ldfld class [System.Collections]System.Collections.Generic.List`1<int32> ForLoopsComparison.AllForLoopBenchmark::Items
        IL_000b: ldloc.0
        IL_000c: callvirt instance !0 class [System.Collections]System.Collections.Generic.List`1<int32>::get_Item(int32)
        IL_0011: call instance void ForLoopsComparison.AllForLoopBenchmark::DoSomeThing(int32)
        // sequence point: (line 38, col 40) to (line 38, col 43) in _
        IL_0016: ldloc.0
        IL_0017: ldc.i4.1
        IL_0018: add
        IL_0019: stloc.0

        // sequence point: (line 38, col 25) to (line 38, col 38) in _
        IL_001a: ldloc.0
        IL_001b: ldarg.0
        IL_001c: call instance int32 ForLoopsComparison.AllForLoopBenchmark::get_ItemCount()
        IL_0021: blt.s IL_0004
    // end loop

    // sequence point: (line 42, col 5) to (line 42, col 6) in _
    IL_0023: ret
}

// .NET 8
.method public hidebysig 
    instance void For () cil managed 
{
    .custom instance void [BenchmarkDotNet.Annotations]BenchmarkDotNet.Attributes.BenchmarkAttribute::.ctor(int32, string) = (
        01 00 23 00 00 00 01 5f 00 00
    )
    // Method begins at RVA 0x207c
    // Code size 36 (0x24)
    .maxstack 3
    .locals init (
        [0] int32 i
    )

    // sequence point: (line 38, col 14) to (line 38, col 23) in _
    IL_0000: ldc.i4.0
    IL_0001: stloc.0
    // sequence point: hidden
    IL_0002: br.s IL_001a
    // loop start (head: IL_001a)
        // sequence point: (line 40, col 13) to (line 40, col 35) in _
        IL_0004: ldarg.0
        IL_0005: ldarg.0
        IL_0006: ldfld class [System.Collections]System.Collections.Generic.List`1<int32> ForLoopsComparison.AllForLoopBenchmark::Items
        IL_000b: ldloc.0
        IL_000c: callvirt instance !0 class [System.Collections]System.Collections.Generic.List`1<int32>::get_Item(int32)
        IL_0011: call instance void ForLoopsComparison.AllForLoopBenchmark::DoSomeThing(int32)
        // sequence point: (line 38, col 40) to (line 38, col 43) in _
        IL_0016: ldloc.0
        IL_0017: ldc.i4.1
        IL_0018: add
        IL_0019: stloc.0

        // sequence point: (line 38, col 25) to (line 38, col 38) in _
        IL_001a: ldloc.0
        IL_001b: ldarg.0
        IL_001c: call instance int32 ForLoopsComparison.AllForLoopBenchmark::get_ItemCount()
        IL_0021: blt.s IL_0004
    // end loop

    // sequence point: (line 42, col 5) to (line 42, col 6) in _
    IL_0023: ret
}
// .NET 6
.method public hidebysig 
    instance void ForEach () cil managed 
{
    .custom instance void [BenchmarkDotNet.Annotations]BenchmarkDotNet.Attributes.BenchmarkAttribute::.ctor(int32, string) = (
        01 00 2c 00 00 00 01 5f 00 00
    )
    // Method begins at RVA 0x20dc
    // Code size 55 (0x37)
    .maxstack 2
    .locals init (
        [0] valuetype [System.Collections]System.Collections.Generic.List`1/Enumerator<int32>,
        [1] int32 item
    )

    // sequence point: (line 47, col 30) to (line 47, col 35) in _
    IL_0000: ldarg.0
    IL_0001: ldfld class [System.Collections]System.Collections.Generic.List`1<int32> ForLoopsComparison.AllForLoopBenchmark::Items
    IL_0006: callvirt instance valuetype [System.Collections]System.Collections.Generic.List`1/Enumerator<!0> class [System.Collections]System.Collections.Generic.List`1<int32>::GetEnumerator()
    IL_000b: stloc.0
    .try
    {
        // sequence point: hidden
        IL_000c: br.s IL_001d
        // loop start (head: IL_001d)
            // sequence point: (line 47, col 18) to (line 47, col 26) in _
            IL_000e: ldloca.s 0
            IL_0010: call instance !0 valuetype [System.Collections]System.Collections.Generic.List`1/Enumerator<int32>::get_Current()
            IL_0015: stloc.1
            // sequence point: (line 49, col 13) to (line 49, col 31) in _
            IL_0016: ldarg.0
            IL_0017: ldloc.1
            IL_0018: call instance void ForLoopsComparison.AllForLoopBenchmark::DoSomeThing(int32)

            // sequence point: (line 47, col 27) to (line 47, col 29) in _
            IL_001d: ldloca.s 0
            IL_001f: call instance bool valuetype [System.Collections]System.Collections.Generic.List`1/Enumerator<int32>::MoveNext()
            IL_0024: brtrue.s IL_000e
        // end loop

        IL_0026: leave.s IL_0036
    }

// .NET 8
.method public hidebysig 
    instance void ForEach () cil managed 
{
    .custom instance void [BenchmarkDotNet.Annotations]BenchmarkDotNet.Attributes.BenchmarkAttribute::.ctor(int32, string) = (
        01 00 2c 00 00 00 01 5f 00 00
    )
    // Method begins at RVA 0x20ac
    // Code size 55 (0x37)
    .maxstack 2
    .locals init (
        [0] valuetype [System.Collections]System.Collections.Generic.List`1/Enumerator<int32>,
        [1] int32 item
    )

    // sequence point: (line 47, col 30) to (line 47, col 35) in _
    IL_0000: ldarg.0
    IL_0001: ldfld class [System.Collections]System.Collections.Generic.List`1<int32> ForLoopsComparison.AllForLoopBenchmark::Items
    IL_0006: callvirt instance valuetype [System.Collections]System.Collections.Generic.List`1/Enumerator<!0> class [System.Collections]System.Collections.Generic.List`1<int32>::GetEnumerator()
    IL_000b: stloc.0
    .try
    {
        // sequence point: hidden
        IL_000c: br.s IL_001d
        // loop start (head: IL_001d)
            // sequence point: (line 47, col 18) to (line 47, col 26) in _
            IL_000e: ldloca.s 0
            IL_0010: call instance !0 valuetype [System.Collections]System.Collections.Generic.List`1/Enumerator<int32>::get_Current()
            IL_0015: stloc.1
            // sequence point: (line 49, col 13) to (line 49, col 31) in _
            IL_0016: ldarg.0
            IL_0017: ldloc.1
            IL_0018: call instance void ForLoopsComparison.AllForLoopBenchmark::DoSomeThing(int32)

            // sequence point: (line 47, col 27) to (line 47, col 29) in _
            IL_001d: ldloca.s 0
            IL_001f: call instance bool valuetype [System.Collections]System.Collections.Generic.List`1/Enumerator<int32>::MoveNext()
            IL_0024: brtrue.s IL_000e
        // end loop

        IL_0026: leave.s IL_0036
    }
// .NET 6
.method public hidebysig 
    instance void ForEach_Linq () cil managed 
{
    .custom instance void [BenchmarkDotNet.Annotations]BenchmarkDotNet.Attributes.BenchmarkAttribute::.ctor(int32, string) = (
        01 00 35 00 00 00 01 5f 00 00
    )
    // Method begins at RVA 0x2130
    // Code size 24 (0x18)
    .maxstack 8

    // sequence point: (line 56, col 9) to (line 56, col 36) in _
    IL_0000: ldarg.0
    IL_0001: ldfld class [System.Collections]System.Collections.Generic.List`1<int32> ForLoopsComparison.AllForLoopBenchmark::Items
    IL_0006: ldarg.0
    IL_0007: ldftn instance void ForLoopsComparison.AllForLoopBenchmark::DoSomeThing(int32)
    IL_000d: newobj instance void class [System.Runtime]System.Action`1<int32>::.ctor(object, native int)
    IL_0012: callvirt instance void class [System.Collections]System.Collections.Generic.List`1<int32>::ForEach(class [System.Runtime]System.Action`1<!0>)
    // sequence point: (line 57, col 5) to (line 57, col 6) in _
    IL_0017: ret
}

// .NET 8
.method public hidebysig 
    instance void ForEach_Linq () cil managed 
{
    .custom instance void [BenchmarkDotNet.Annotations]BenchmarkDotNet.Attributes.BenchmarkAttribute::.ctor(int32, string) = (
        01 00 35 00 00 00 01 5f 00 00
    )
    // Method begins at RVA 0x2100
    // Code size 24 (0x18)
    .maxstack 8

    // sequence point: (line 56, col 9) to (line 56, col 36) in _
    IL_0000: ldarg.0
    IL_0001: ldfld class [System.Collections]System.Collections.Generic.List`1<int32> ForLoopsComparison.AllForLoopBenchmark::Items
    IL_0006: ldarg.0
    IL_0007: ldftn instance void ForLoopsComparison.AllForLoopBenchmark::DoSomeThing(int32)
    IL_000d: newobj instance void class [System.Runtime]System.Action`1<int32>::.ctor(object, native int)
    IL_0012: callvirt instance void class [System.Collections]System.Collections.Generic.List`1<int32>::ForEach(class [System.Runtime]System.Action`1<!0>)
    // sequence point: (line 57, col 5) to (line 57, col 6) in _
    IL_0017: ret
}
// .NET 6
.method public hidebysig 
    instance void ForEach_Parallel () cil managed 
{
    .custom instance void [BenchmarkDotNet.Annotations]BenchmarkDotNet.Attributes.BenchmarkAttribute::.ctor(int32, string) = (
        01 00 3b 00 00 00 01 5f 00 00
    )
    // Method begins at RVA 0x2149
    // Code size 25 (0x19)
    .maxstack 8

    // sequence point: (line 62, col 9) to (line 62, col 46) in _
    IL_0000: ldarg.0
    IL_0001: ldfld class [System.Collections]System.Collections.Generic.List`1<int32> ForLoopsComparison.AllForLoopBenchmark::Items
    IL_0006: ldarg.0
    IL_0007: ldftn instance void ForLoopsComparison.AllForLoopBenchmark::DoSomeThing(int32)
    IL_000d: newobj instance void class [System.Runtime]System.Action`1<int32>::.ctor(object, native int)
    IL_0012: call valuetype [System.Threading.Tasks.Parallel]System.Threading.Tasks.ParallelLoopResult [System.Threading.Tasks.Parallel]System.Threading.Tasks.Parallel::ForEach<int32>(class [System.Runtime]System.Collections.Generic.IEnumerable`1<!!0>, class [System.Runtime]System.Action`1<!!0>)
    IL_0017: pop
    // sequence point: (line 63, col 5) to (line 63, col 6) in _
    IL_0018: ret
}

// .NET 8
.method public hidebysig 
    instance void ForEach_Parallel () cil managed 
{
    .custom instance void [BenchmarkDotNet.Annotations]BenchmarkDotNet.Attributes.BenchmarkAttribute::.ctor(int32, string) = (
        01 00 3b 00 00 00 01 5f 00 00
    )
    // Method begins at RVA 0x2119
    // Code size 25 (0x19)
    .maxstack 8

    // sequence point: (line 62, col 9) to (line 62, col 46) in _
    IL_0000: ldarg.0
    IL_0001: ldfld class [System.Collections]System.Collections.Generic.List`1<int32> ForLoopsComparison.AllForLoopBenchmark::Items
    IL_0006: ldarg.0
    IL_0007: ldftn instance void ForLoopsComparison.AllForLoopBenchmark::DoSomeThing(int32)
    IL_000d: newobj instance void class [System.Runtime]System.Action`1<int32>::.ctor(object, native int)
    IL_0012: call valuetype [System.Threading.Tasks.Parallel]System.Threading.Tasks.ParallelLoopResult [System.Threading.Tasks.Parallel]System.Threading.Tasks.Parallel::ForEach<int32>(class [System.Runtime]System.Collections.Generic.IEnumerable`1<!!0>, class [System.Runtime]System.Action`1<!!0>)
    IL_0017: pop
    // sequence point: (line 63, col 5) to (line 63, col 6) in _
    IL_0018: ret
}
// .NET 6
.method public hidebysig 
    instance void ForEach_LinqParallel () cil managed 
{
    .custom instance void [BenchmarkDotNet.Annotations]BenchmarkDotNet.Attributes.BenchmarkAttribute::.ctor(int32, string) = (
        01 00 41 00 00 00 01 5f 00 00
    )
    // Method begins at RVA 0x2163
    // Code size 29 (0x1d)
    .maxstack 8

    // sequence point: (line 68, col 9) to (line 68, col 48) in _
    IL_0000: ldarg.0
    IL_0001: ldfld class [System.Collections]System.Collections.Generic.List`1<int32> ForLoopsComparison.AllForLoopBenchmark::Items
    IL_0006: call class [System.Linq.Parallel]System.Linq.ParallelQuery`1<!!0> [System.Linq.Parallel]System.Linq.ParallelEnumerable::AsParallel<int32>(class [System.Runtime]System.Collections.Generic.IEnumerable`1<!!0>)
    IL_000b: ldarg.0
    IL_000c: ldftn instance void ForLoopsComparison.AllForLoopBenchmark::DoSomeThing(int32)
    IL_0012: newobj instance void class [System.Runtime]System.Action`1<int32>::.ctor(object, native int)
    IL_0017: call void [System.Linq.Parallel]System.Linq.ParallelEnumerable::ForAll<int32>(class [System.Linq.Parallel]System.Linq.ParallelQuery`1<!!0>, class [System.Runtime]System.Action`1<!!0>)
    // sequence point: (line 69, col 5) to (line 69, col 6) in _
    IL_001c: ret
}

// .NET 8
.method public hidebysig 
    instance void ForEach_LinqParallel () cil managed 
{
    .custom instance void [BenchmarkDotNet.Annotations]BenchmarkDotNet.Attributes.BenchmarkAttribute::.ctor(int32, string) = (
        01 00 41 00 00 00 01 5f 00 00
    )
    // Method begins at RVA 0x2133
    // Code size 29 (0x1d)
    .maxstack 8

    // sequence point: (line 68, col 9) to (line 68, col 48) in _
    IL_0000: ldarg.0
    IL_0001: ldfld class [System.Collections]System.Collections.Generic.List`1<int32> ForLoopsComparison.AllForLoopBenchmark::Items
    IL_0006: call class [System.Linq.Parallel]System.Linq.ParallelQuery`1<!!0> [System.Linq.Parallel]System.Linq.ParallelEnumerable::AsParallel<int32>(class [System.Runtime]System.Collections.Generic.IEnumerable`1<!!0>)
    IL_000b: ldarg.0
    IL_000c: ldftn instance void ForLoopsComparison.AllForLoopBenchmark::DoSomeThing(int32)
    IL_0012: newobj instance void class [System.Runtime]System.Action`1<int32>::.ctor(object, native int)
    IL_0017: call void [System.Linq.Parallel]System.Linq.ParallelEnumerable::ForAll<int32>(class [System.Linq.Parallel]System.Linq.ParallelQuery`1<!!0>, class [System.Runtime]System.Action`1<!!0>)
    // sequence point: (line 69, col 5) to (line 69, col 6) in _
    IL_001c: ret
}
// .NET 6
.method public hidebysig 
    instance void For_Span () cil managed 
{
    .custom instance void [BenchmarkDotNet.Annotations]BenchmarkDotNet.Attributes.BenchmarkAttribute::.ctor(int32, string) = (
        01 00 47 00 00 00 01 5f 00 00
    )
    // Method begins at RVA 0x2184
    // Code size 46 (0x2e)
    .maxstack 3
    .locals init (
        [0] valuetype [System.Runtime]System.Span`1<int32> span,
        [1] int32 i
    )

    // sequence point: (line 74, col 9) to (line 74, col 53) in _
    IL_0000: ldarg.0
    IL_0001: ldfld class [System.Collections]System.Collections.Generic.List`1<int32> ForLoopsComparison.AllForLoopBenchmark::Items
    IL_0006: call valuetype [System.Runtime]System.Span`1<!!0> [System.Runtime.InteropServices]System.Runtime.InteropServices.CollectionsMarshal::AsSpan<int32>(class [System.Collections]System.Collections.Generic.List`1<!!0>)
    IL_000b: stloc.0
    // sequence point: (line 75, col 14) to (line 75, col 23) in _
    IL_000c: ldc.i4.0
    IL_000d: stloc.1
    // sequence point: hidden
    IL_000e: br.s IL_0023
    // loop start (head: IL_0023)
        // sequence point: (line 77, col 13) to (line 77, col 34) in _
        IL_0010: ldarg.0
        IL_0011: ldloca.s 0
        IL_0013: ldloc.1
        IL_0014: call instance !0& valuetype [System.Runtime]System.Span`1<int32>::get_Item(int32)
        IL_0019: ldind.i4
        IL_001a: call instance void ForLoopsComparison.AllForLoopBenchmark::DoSomeThing(int32)
        // sequence point: (line 75, col 42) to (line 75, col 45) in _
        IL_001f: ldloc.1
        IL_0020: ldc.i4.1
        IL_0021: add
        IL_0022: stloc.1

        // sequence point: (line 75, col 25) to (line 75, col 40) in _
        IL_0023: ldloc.1
        IL_0024: ldloca.s 0
        IL_0026: call instance int32 valuetype [System.Runtime]System.Span`1<int32>::get_Length()
        IL_002b: blt.s IL_0010
    // end loop

    // sequence point: (line 79, col 5) to (line 79, col 6) in _
    IL_002d: ret
}

// .NET 8
.method public hidebysig 
    instance void For_Span () cil managed 
{
    .custom instance void [BenchmarkDotNet.Annotations]BenchmarkDotNet.Attributes.BenchmarkAttribute::.ctor(int32, string) = (
        01 00 47 00 00 00 01 5f 00 00
    )
    // Method begins at RVA 0x2154
    // Code size 46 (0x2e)
    .maxstack 3
    .locals init (
        [0] valuetype [System.Runtime]System.Span`1<int32> span,
        [1] int32 i
    )

    // sequence point: (line 74, col 9) to (line 74, col 53) in _
    IL_0000: ldarg.0
    IL_0001: ldfld class [System.Collections]System.Collections.Generic.List`1<int32> ForLoopsComparison.AllForLoopBenchmark::Items
    IL_0006: call valuetype [System.Runtime]System.Span`1<!!0> [System.Runtime.InteropServices]System.Runtime.InteropServices.CollectionsMarshal::AsSpan<int32>(class [System.Collections]System.Collections.Generic.List`1<!!0>)
    IL_000b: stloc.0
    // sequence point: (line 75, col 14) to (line 75, col 23) in _
    IL_000c: ldc.i4.0
    IL_000d: stloc.1
    // sequence point: hidden
    IL_000e: br.s IL_0023
    // loop start (head: IL_0023)
        // sequence point: (line 77, col 13) to (line 77, col 34) in _
        IL_0010: ldarg.0
        IL_0011: ldloca.s 0
        IL_0013: ldloc.1
        IL_0014: call instance !0& valuetype [System.Runtime]System.Span`1<int32>::get_Item(int32)
        IL_0019: ldind.i4
        IL_001a: call instance void ForLoopsComparison.AllForLoopBenchmark::DoSomeThing(int32)
        // sequence point: (line 75, col 42) to (line 75, col 45) in _
        IL_001f: ldloc.1
        IL_0020: ldc.i4.1
        IL_0021: add
        IL_0022: stloc.1

        // sequence point: (line 75, col 25) to (line 75, col 40) in _
        IL_0023: ldloc.1
        IL_0024: ldloca.s 0
        IL_0026: call instance int32 valuetype [System.Runtime]System.Span`1<int32>::get_Length()
        IL_002b: blt.s IL_0010
    // end loop

    // sequence point: (line 79, col 5) to (line 79, col 6) in _
    IL_002d: ret
}
// .NET 6
.method public hidebysig 
    instance void ForEach_Span () cil managed 
{
    .custom instance void [BenchmarkDotNet.Annotations]BenchmarkDotNet.Attributes.BenchmarkAttribute::.ctor(int32, string) = (
        01 00 51 00 00 00 01 5f 00 00
    )
    // Method begins at RVA 0x21c0
    // Code size 48 (0x30)
    .maxstack 2
    .locals init (
        [0] valuetype [System.Runtime]System.Span`1<int32>,
        [1] int32,
        [2] int32 item
    )

    // sequence point: (line 84, col 30) to (line 84, col 62) in _
    IL_0000: ldarg.0
    IL_0001: ldfld class [System.Collections]System.Collections.Generic.List`1<int32> ForLoopsComparison.AllForLoopBenchmark::Items
    IL_0006: call valuetype [System.Runtime]System.Span`1<!!0> [System.Runtime.InteropServices]System.Runtime.InteropServices.CollectionsMarshal::AsSpan<int32>(class [System.Collections]System.Collections.Generic.List`1<!!0>)
    IL_000b: stloc.0
    IL_000c: ldc.i4.0
    IL_000d: stloc.1
    // sequence point: hidden
    IL_000e: br.s IL_0025
    // loop start (head: IL_0025)
        // sequence point: (line 84, col 18) to (line 84, col 26) in _
        IL_0010: ldloca.s 0
        IL_0012: ldloc.1
        IL_0013: call instance !0& valuetype [System.Runtime]System.Span`1<int32>::get_Item(int32)
        IL_0018: ldind.i4
        IL_0019: stloc.2
        // sequence point: (line 86, col 13) to (line 86, col 31) in _
        IL_001a: ldarg.0
        IL_001b: ldloc.2
        IL_001c: call instance void ForLoopsComparison.AllForLoopBenchmark::DoSomeThing(int32)
        // sequence point: hidden
        IL_0021: ldloc.1
        IL_0022: ldc.i4.1
        IL_0023: add
        IL_0024: stloc.1

        // sequence point: (line 84, col 27) to (line 84, col 29) in _
        IL_0025: ldloc.1
        IL_0026: ldloca.s 0
        IL_0028: call instance int32 valuetype [System.Runtime]System.Span`1<int32>::get_Length()
        IL_002d: blt.s IL_0010
    // end loop

    // sequence point: (line 88, col 5) to (line 88, col 6) in _
    IL_002f: ret
}

// .NET 8
.method public hidebysig 
    instance void ForEach_Span () cil managed 
{
    .custom instance void [BenchmarkDotNet.Annotations]BenchmarkDotNet.Attributes.BenchmarkAttribute::.ctor(int32, string) = (
        01 00 51 00 00 00 01 5f 00 00
    )
    // Method begins at RVA 0x2190
    // Code size 48 (0x30)
    .maxstack 2
    .locals init (
        [0] valuetype [System.Runtime]System.Span`1<int32>,
        [1] int32,
        [2] int32 item
    )

    // sequence point: (line 84, col 30) to (line 84, col 62) in _
    IL_0000: ldarg.0
    IL_0001: ldfld class [System.Collections]System.Collections.Generic.List`1<int32> ForLoopsComparison.AllForLoopBenchmark::Items
    IL_0006: call valuetype [System.Runtime]System.Span`1<!!0> [System.Runtime.InteropServices]System.Runtime.InteropServices.CollectionsMarshal::AsSpan<int32>(class [System.Collections]System.Collections.Generic.List`1<!!0>)
    IL_000b: stloc.0
    IL_000c: ldc.i4.0
    IL_000d: stloc.1
    // sequence point: hidden
    IL_000e: br.s IL_0025
    // loop start (head: IL_0025)
        // sequence point: (line 84, col 18) to (line 84, col 26) in _
        IL_0010: ldloca.s 0
        IL_0012: ldloc.1
        IL_0013: call instance !0& valuetype [System.Runtime]System.Span`1<int32>::get_Item(int32)
        IL_0018: ldind.i4
        IL_0019: stloc.2
        // sequence point: (line 86, col 13) to (line 86, col 31) in _
        IL_001a: ldarg.0
        IL_001b: ldloc.2
        IL_001c: call instance void ForLoopsComparison.AllForLoopBenchmark::DoSomeThing(int32)
        // sequence point: hidden
        IL_0021: ldloc.1
        IL_0022: ldc.i4.1
        IL_0023: add
        IL_0024: stloc.1

        // sequence point: (line 84, col 27) to (line 84, col 29) in _
        IL_0025: ldloc.1
        IL_0026: ldloca.s 0
        IL_0028: call instance int32 valuetype [System.Runtime]System.Span`1<int32>::get_Length()
        IL_002d: blt.s IL_0010
    // end loop

    // sequence point: (line 88, col 5) to (line 88, col 6) in _
    IL_002f: ret
}

// .NET 6 (X64)
For()
    L0000: sub rsp, 0x28
    L0004: xor eax, eax
    L0006: mov edx, [rcx+0x10]
    L0009: test edx, edx
    L000b: jle short L002a
    L000d: mov rcx, [rcx+8]
    L0011: mov r8, rcx
    L0014: cmp eax, [r8+0x10]
    L0018: jae short L002f
    L001a: mov r8, [r8+8]
    L001e: cmp eax, [r8+8]
    L0022: jae short L0035
    L0024: inc eax
    L0026: cmp eax, edx
    L0028: jl short L0011
    L002a: add rsp, 0x28
    L002e: ret
    L002f: call System.ThrowHelper.ThrowArgumentOutOfRange_IndexException()
    L0034: int3
    L0035: call 0x00007ffccec4f540
    L003a: int3
// .NET 8 (X64)
For()
    L0000: sub rsp, 0x28
    L0004: xor eax, eax
    L0006: mov edx, [rcx+0x10]
    L0009: test edx, edx
    L000b: jle short L0039
    L000d: mov rcx, [rcx+8]
    L0011: nop [rax]
    L0018: nop [rax+rax]
    L0020: mov r8, rcx
    L0023: cmp eax, [r8+0x10]
    L0027: jae short L003e
    L0029: mov r8, [r8+8]
    L002d: cmp eax, [r8+8]
    L0031: jae short L0045
    L0033: inc eax
    L0035: cmp eax, edx
    L0037: jl short L0020
    L0039: add rsp, 0x28
    L003d: ret
    L003e: call qword ptr [0x7ffba997e508]
    L0044: int3
    L0045: call 0x00007ffc092a0da0
    L004a: int3
// .NET 6 (X64)
ForEach()
    L0000: sub rsp, 0x28
    L0004: mov rax, [rcx+8]
    L0008: mov edx, [rax+0x14]
    L000b: mov ecx, edx
    L000d: xor r8d, r8d
    L0010: cmp ecx, edx
    L0012: jne short L0043
    L0014: cmp r8d, [rax+0x10]
    L0018: jae short L0037
    L001a: mov r9, [rax+8]
    L001e: cmp r8d, [r9+8]
    L0022: jae short L0049
    L0024: inc r8d
    L0027: mov r9d, 1
    L002d: test r9d, r9d
    L0030: jne short L0010
    L0032: add rsp, 0x28
    L0036: ret
    L0037: mov r8d, [rax+0x10]
    L003b: inc r8d
    L003e: xor r9d, r9d
    L0041: jmp short L002d
    L0043: call System.ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumFailedVersion()
    L0048: int3
    L0049: call 0x00007ffccec4f540
    L004e: int3
// .NET 8 (X64)
ForEach()
    L0000: sub rsp, 0x28
    L0004: mov rax, [rcx+8]
    L0008: mov ecx, [rax+0x14]
    L000b: xor ecx, ecx
    L000d: cmp ecx, [rax+0x10]
    L0010: jae short L001f
    L0012: mov rdx, [rax+8]
    L0016: cmp ecx, [rdx+8]
    L0019: jae short L0024
    L001b: inc ecx
    L001d: jmp short L000d
    L001f: add rsp, 0x28
    L0023: ret
    L0024: call 0x00007ffc092a0da0
    L0029: int3
// .NET 6 (X64)
ForEach_Linq()
    L0000: push rdi
    L0001: push rsi
    L0002: push rbx
    L0003: sub rsp, 0x20
    L0007: mov rsi, rcx
    L000a: mov rcx, 0x7ffc723874c8
    L0014: call 0x00007ffcceb2c310
    L0019: mov rdi, rax
    L001c: mov rbx, [rsi+8]
    L0020: lea rcx, [rdi+8]
    L0024: mov rdx, rsi
    L0027: call 0x00007ffcceb2bef0
    L002c: mov rcx, ForLoopsComparison.AllForLoopBenchmark.DoSomeThing(Int32)
    L0036: mov [rdi+0x18], rcx
    L003a: mov rcx, rbx
    L003d: mov rdx, rdi
    L0040: cmp [rcx], ecx
    L0042: add rsp, 0x20
    L0046: pop rbx
    L0047: pop rsi
    L0048: pop rdi
    L0049: jmp System.Collections.Generic.List`1[[System.Int32, System.Private.CoreLib]].ForEach(System.Action`1<Int32>)
// .NET 8 (X64)
ForEach_Linq()
    L0000: push rdi
    L0001: push rsi
    L0002: push rbx
    L0003: sub rsp, 0x20
    L0007: mov rbx, rcx
    L000a: mov rcx, 0x7ffbae1c5258
    L0014: call 0x00007ffc0917ae10
    L0019: mov rsi, rax
    L001c: mov rdi, [rbx+8]
    L0020: lea rcx, [rsi+8]
    L0024: mov rdx, rbx
    L0027: call 0x00007ffc0917a680
    L002c: mov rcx, ForLoopsComparison.AllForLoopBenchmark.DoSomeThing(Int32)
    L0036: mov [rsi+0x18], rcx
    L003a: mov rcx, rdi
    L003d: mov rdx, rsi
    L0040: cmp [rcx], ecx
    L0042: add rsp, 0x20
    L0046: pop rbx
    L0047: pop rsi
    L0048: pop rdi
    L0049: jmp qword ptr [0x7ffba973f000]
// .NET 6 (X64)
ForEach_Parallel()
    L0000: push rdi
    L0001: push rsi
    L0002: push rbx
    L0003: sub rsp, 0x40
    L0007: mov rsi, rcx
    L000a: mov rcx, 0x7ffc723874c8
    L0014: call 0x00007ffcceb2c310
    L0019: mov rdi, rax
    L001c: mov rbx, [rsi+8]
    L0020: lea rcx, [rdi+8]
    L0024: mov rdx, rsi
    L0027: call 0x00007ffcceb2bef0
    L002c: mov rcx, ForLoopsComparison.AllForLoopBenchmark.DoSomeThing(Int32)
    L0036: mov [rdi+0x18], rcx
    L003a: lea rcx, [rsp+0x28]
    L003f: mov rdx, rbx
    L0042: mov r8, rdi
    L0045: call System.Threading.Tasks.Parallel.ForEach[[System.Int32, System.Private.CoreLib]](System.Collections.Generic.IEnumerable`1<Int32>, System.Action`1<Int32>)
    L004a: nop
    L004b: add rsp, 0x40
    L004f: pop rbx
    L0050: pop rsi
    L0051: pop rdi
    L0052: ret
// .NET 8 (X64)
ForEach_Parallel()
    L0000: push rbp
    L0001: push rdi
    L0002: push rsi
    L0003: push rbx
    L0004: sub rsp, 0x78
    L0008: lea rbp, [rsp+0x90]
    L0010: xor eax, eax
    L0012: mov [rbp-0x38], rax
    L0016: mov rbx, rcx
    L0019: mov rcx, 0x7ffbae1c5258
    L0023: call 0x00007ffc0917ae10
    L0028: mov rsi, rax
    L002b: mov rdi, [rbx+8]
    L002f: lea rcx, [rsi+8]
    L0033: mov rdx, rbx
    L0036: call 0x00007ffc0917a680
    L003b: mov rdx, ForLoopsComparison.AllForLoopBenchmark.DoSomeThing(Int32)
    L0045: mov [rsi+0x18], rdx
    L0049: test rdi, rdi
    L004c: je L012b
    L0052: test byte ptr [0x7ffbae1ca76c], 1
    L0059: je L0149
    L005f: mov rdx, 0x1f868c415d0
    L0069: mov rbx, [rdx]
    L006c: mov rdx, [rbx+0x18]
    L0070: mov [rbp-0x38], rdx
    L0074: cmp qword ptr [rbp-0x38], 0
    L0079: je short L0089
    L007b: mov rdx, [rbp-0x38]
    L007f: cmp dword ptr [rdx+0x20], 0
    L0083: jne L0162
    L0089: mov rdx, rdi
    L008c: mov rcx, 0x7ffba95e9df8
    L0096: call qword ptr [0x7ffba95f4330]
    L009c: test rax, rax
    L009f: je short L00e2
    L00a1: mov [rsp+0x20], rsi
    L00a6: xor ecx, ecx
    L00a8: mov [rsp+0x28], rcx
    L00ad: mov [rsp+0x30], rcx
    L00b2: mov [rsp+0x38], rcx
    L00b7: mov [rsp+0x40], rcx
    L00bc: mov [rsp+0x48], rcx
    L00c1: mov [rsp+0x50], rcx
    L00c6: lea rcx, [rbp-0x30]
    L00ca: mov r8, rax
    L00cd: mov r9, rbx
    L00d0: mov rdx, 0x7ffbb2114d78
    L00da: call qword ptr [0x7ffbb21243a8]
    L00e0: jmp short L0121
    L00e2: mov r8, rdi
    L00e5: mov [rsp+0x20], rsi
    L00ea: xor ecx, ecx
    L00ec: mov [rsp+0x28], rcx
    L00f1: mov [rsp+0x30], rcx
    L00f6: mov [rsp+0x38], rcx
    L00fb: mov [rsp+0x40], rcx
    L0100: mov [rsp+0x48], rcx
    L0105: mov [rsp+0x50], rcx
    L010a: lea rcx, [rbp-0x30]
    L010e: mov r9, rbx
    L0111: mov rdx, 0x7ffbb2114e80
    L011b: call qword ptr [0x7ffbb21243d8]
    L0121: nop
    L0122: add rsp, 0x78
    L0126: pop rbx
    L0127: pop rsi
    L0128: pop rdi
    L0129: pop rbp
    L012a: ret
    L012b: mov ecx, 0x3c3
    L0130: mov rdx, 0x7ffbae1c8c78
    L013a: call 0x00007ffc09177650
    L013f: mov rcx, rax
    L0142: call qword ptr [0x7ffba9716790]
    L0148: int3
    L0149: mov rcx, 0x7ffbae1ca738
    L0153: mov edx, 4
    L0158: call 0x00007ffc0917afe0
    L015d: jmp L005f
    L0162: lea rcx, [rbp-0x38]
    L0166: call qword ptr [0x7ffba98a43d8]
    L016c: int3
// .NET 6 (X64)
ForEach_LinqParallel()
    L0000: push rdi
    L0001: push rsi
    L0002: push rbx
    L0003: sub rsp, 0x20
    L0007: mov rsi, rcx
    L000a: mov rcx, 0x7ffc723874c8
    L0014: call 0x00007ffcceb2c310
    L0019: mov rdi, rax
    L001c: mov rcx, [rsi+8]
    L0020: call System.Linq.ParallelEnumerable.AsParallel[[System.Int32, System.Private.CoreLib]](System.Collections.Generic.IEnumerable`1<Int32>)
    L0025: mov rbx, rax
    L0028: lea rcx, [rdi+8]
    L002c: mov rdx, rsi
    L002f: call 0x00007ffcceb2bef0
    L0034: mov rcx, ForLoopsComparison.AllForLoopBenchmark.DoSomeThing(Int32)
    L003e: mov [rdi+0x18], rcx
    L0042: mov rcx, rbx
    L0045: mov rdx, rdi
    L0048: add rsp, 0x20
    L004c: pop rbx
    L004d: pop rsi
    L004e: pop rdi
    L004f: jmp System.Linq.ParallelEnumerable.ForAll[[System.Int32, System.Private.CoreLib]](System.Linq.ParallelQuery`1<Int32>, System.Action`1<Int32>)
// .NET 8 (X64)
ForEach_LinqParallel()
    L0000: push rdi
    L0001: push rsi
    L0002: push rbx
    L0003: sub rsp, 0x20
    L0007: mov rbx, rcx
    L000a: mov rcx, 0x7ffbae1c5258
    L0014: call 0x00007ffc0917ae10
    L0019: mov rsi, rax
    L001c: mov rcx, [rbx+8]
    L0020: call qword ptr [0x7ffbb2126118]
    L0026: mov rdi, rax
    L0029: lea rcx, [rsi+8]
    L002d: mov rdx, rbx
    L0030: call 0x00007ffc0917a680
    L0035: mov rcx, ForLoopsComparison.AllForLoopBenchmark.DoSomeThing(Int32)
    L003f: mov [rsi+0x18], rcx
    L0043: mov rcx, rdi
    L0046: mov rdx, rsi
    L0049: add rsp, 0x20
    L004d: pop rbx
    L004e: pop rsi
    L004f: pop rdi
    L0050: jmp qword ptr [0x7ffbb21266d0]
// .NET 6 (X64)
For_Span()
    L0000: sub rsp, 0x28
    L0004: mov rax, [rcx+8]
    L0008: test rax, rax
    L000b: je short L002a
    L000d: mov rdx, [rax+8]
    L0011: mov eax, [rax+0x10]
    L0014: test rdx, rdx
    L0017: je short L0052
    L0019: mov ecx, [rdx+8]
    L001c: mov r8d, eax
    L001f: cmp rcx, r8
    L0022: jb short L005c
    L0024: add rdx, 0x10
    L0028: jmp short L002e
    L002a: xor edx, edx
    L002c: xor eax, eax
    L002e: xor ecx, ecx
    L0030: test eax, eax
    L0032: jle short L004d
    L0034: nop [rax]
    L0038: nop [rax+rax]
    L0040: movsxd r8, ecx
    L0043: mov r8d, [rdx+r8*4]
    L0047: inc ecx
    L0049: cmp ecx, eax
    L004b: jl short L0040
    L004d: add rsp, 0x28
    L0051: ret
    L0052: test eax, eax
    L0054: jne short L005c
    L0056: xor edx, edx
    L0058: xor eax, eax
    L005a: jmp short L0028
    L005c: call System.ThrowHelper.ThrowArgumentOutOfRangeException()
    L0061: int3
// .NET 8 (X64)
For_Span()
    L0000: sub rsp, 0x28
    L0004: mov rax, [rcx+8]
    L0008: test rax, rax
    L000b: je short L0024
    L000d: mov rcx, [rax+8]
    L0011: mov eax, [rax+0x10]
    L0014: test rcx, rcx
    L0017: je short L0040
    L0019: cmp [rcx+8], eax
    L001c: jb short L004a
    L001e: add rcx, 0x10
    L0022: jmp short L0028
    L0024: xor ecx, ecx
    L0026: xor eax, eax
    L0028: xor edx, edx
    L002a: test eax, eax
    L002c: jle short L003b
    L002e: mov r8d, edx
    L0031: mov r8d, [rcx+r8*4]
    L0035: inc edx
    L0037: cmp edx, eax
    L0039: jl short L002e
    L003b: add rsp, 0x28
    L003f: ret
    L0040: test eax, eax
    L0042: jne short L004a
    L0044: xor ecx, ecx
    L0046: xor eax, eax
    L0048: jmp short L0022
    L004a: call qword ptr [0x7ffba997e478]
    L0050: int3
// .NET 6 (X64)
ForEach_Span()
    L0000: sub rsp, 0x28
    L0004: mov rax, [rcx+8]
    L0008: test rax, rax
    L000b: je short L002a
    L000d: mov rdx, [rax+8]
    L0011: mov eax, [rax+0x10]
    L0014: test rdx, rdx
    L0017: je short L0052
    L0019: mov ecx, [rdx+8]
    L001c: mov r8d, eax
    L001f: cmp rcx, r8
    L0022: jb short L005c
    L0024: add rdx, 0x10
    L0028: jmp short L002e
    L002a: xor edx, edx
    L002c: xor eax, eax
    L002e: xor ecx, ecx
    L0030: test eax, eax
    L0032: jle short L004d
    L0034: nop [rax]
    L0038: nop [rax+rax]
    L0040: movsxd r8, ecx
    L0043: mov r8d, [rdx+r8*4]
    L0047: inc ecx
    L0049: cmp ecx, eax
    L004b: jl short L0040
    L004d: add rsp, 0x28
    L0051: ret
    L0052: test eax, eax
    L0054: jne short L005c
    L0056: xor edx, edx
    L0058: xor eax, eax
    L005a: jmp short L0028
    L005c: call System.ThrowHelper.ThrowArgumentOutOfRangeException()
    L0061: int3
// .NET 8 (X64)
ForEach_Span()
    L0000: sub rsp, 0x28
    L0004: mov rax, [rcx+8]
    L0008: test rax, rax
    L000b: je short L0024
    L000d: mov rcx, [rax+8]
    L0011: mov eax, [rax+0x10]
    L0014: test rcx, rcx
    L0017: je short L0040
    L0019: cmp [rcx+8], eax
    L001c: jb short L004a
    L001e: add rcx, 0x10
    L0022: jmp short L0028
    L0024: xor ecx, ecx
    L0026: xor eax, eax
    L0028: xor edx, edx
    L002a: test eax, eax
    L002c: jle short L003b
    L002e: mov r8d, edx
    L0031: mov r8d, [rcx+r8*4]
    L0035: inc edx
    L0037: cmp edx, eax
    L0039: jl short L002e
    L003b: add rsp, 0x28
    L003f: ret
    L0040: test eax, eax
    L0042: jne short L004a
    L0044: xor ecx, ecx
    L0046: xor eax, eax
    L0048: jmp short L0022
    L004a: call qword ptr [0x7ffba997e478]
    L0050: int3


Benchmark Description:


A Comparison about C# For loops: .NET 9 is slower than .NET 8 in some methods Just out of curiosity, I made this benchmark for checking the performance, and as we know the pure For loops is a winner. The interesting thing for me was that .NET 9 that was slower than .NET 8 in some methods like ForEach_LinqParallel. Although these numbers are negligible still fun to know.

The provided benchmark code is designed to compare the performance of different looping constructs in C# across multiple versions of the .NET runtime. It aims to measure the efficiency of iterating over a collection in various ways, focusing on execution time and, indirectly, memory usage (though memory diagnosing is explicitly turned off in this setup). This benchmark setup can reveal insights into how different looping mechanisms perform under the same conditions and how performance might vary across different .NET versions. ### General Setup Overview - **.NET Versions:** The benchmarks are set to run on .NET 6.0, .NET 8.0, and .NET 9.0, as indicated by the `RuntimeMoniker` attributes. This allows for performance comparison across these runtime versions. - **Memory Diagnoser:** Disabled (`[MemoryDiagnoser(false)]`), indicating that memory allocation metrics are not being collected. - **ItemCount Parameter:** The benchmarks are parameterized to run with a collection size of 100,000 items. This provides a substantial workload to measure the performance differences between the loop constructs. - **Global Setup:** Before each benchmark, a list of integers (`Items`) is populated with 100,000 sequential integers starting from 0. This setup ensures that each benchmark method operates on the same data. ### Benchmark Methods and Rationale 1. **For Loop (`For`):** - **Purpose:** Measures the performance of a traditional `for` loop accessing elements by index. - **Importance:** Serves as a baseline for performance comparison. Direct indexing is typically fast but depends on the collection type. - **Expected Insights:** Generally, direct indexing should be efficient, but the results will show how it compares to other methods. 2. **ForEach Loop (`ForEach`):** - **Purpose:** Evaluates the performance of the `foreach` loop construct over a `List<T>`. - **Importance:** `foreach` is widely used for its readability and convenience, but it may introduce overhead compared to index-based access. - **Expected Insights:** Likely to be slightly slower than direct indexing due to enumerator overhead. 3. **ForEach with LINQ (`ForEach_Linq`):** - **Purpose:** Tests the performance of using `List<T>.ForEach` with a method delegate. - **Importance:** This method is often used for its concise syntax but may have performance implications. - **Expected Insights:** This could introduce additional overhead compared to traditional loops, especially due to delegate invocation. 4. **Parallel.ForEach (`ForEach_Parallel`):** - **Purpose:** Measures how `Parallel.ForEach` performs with the list, aiming to utilize multiple threads. - **Importance:** Important for understanding the benefits and overhead of parallel processing on collections. - **Expected Insights:** Should show significant performance improvement on multi-core systems, but with overhead for coordination between threads. 5. **Parallel LINQ (`ForEach_LinqParallel`):** - **Purpose:** Evaluates the performance of parallel processing using PLINQ. - **Importance:** Offers a LINQ-friendly way to parallelize operations, balancing ease of use with performance. - **Expected Insights:** Similar to `Parallel.ForEach`, but the overhead might vary due to PLINQ's query optimizations. 6. **For Loop with Span (`For_Span`):** - **Purpose:** Tests the performance of iterating over a `Span<T>` created from the list. - **Importance:** `Span<T>` is designed for performance-critical scenarios, offering a way to access collections without heap allocations. - **Expected Insights:** Expected to be among the fastest, especially for value types, due to reduced overhead and memory efficiency. 7. **ForEach Loop with Span (`ForEach_Span`):** - **Purpose:** Measures the performance of using `foreach` over a `Span<T>`. - **Importance:** Combines the convenience of `foreach` with the performance benefits of `Span<T>`. - **Expected Insights:** Likely to offer improved performance over regular `foreach` on lists, but with some overhead compared to a `for` loop with `Span<T>`. ### Conclusion Running these benchmarks will provide valuable insights into the performance characteristics of different looping constructs in C#. The results will highlight the trade-offs between ease of use, readability, and execution speed, especially across different .NET runtime versions. Understanding these trade-offs is crucial for writing high-performance C# code, particularly in scenarios where performance is critical.


Benchmark Comments: