2


Linq Aggregate Functions in .NET 8 & 9




Date Added (UTC):

01 May 2024 @ 10:16

Date Updated (UTC):

01 May 2024 @ 10:16


.NET Version(s):

.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;

namespace Benchmarks;

[SimpleJob(RuntimeMoniker.Net80, baseline: true)]
[SimpleJob(RuntimeMoniker.Net90)]
[MemoryDiagnoser(false)]
[HideColumns("RatioSD", "Median", "StdDev", "Error", "Job", "Alloc Ratio")]
public class LinqAggregateBenchmark
{
    [Params(10000)]
    public int Length { get; set; }

    private IEnumerable<int> _source;

    [GlobalSetup]
    public void Setup() => _source = Enumerable.Range(1, Length);

    [Benchmark]
    public int Min() => _source.Min();

    [Benchmark]
    public int Max() => _source.Max();

    [Benchmark]
    public double Average() => _source.Average();

    [Benchmark]
    public int Sum() => _source.Sum();
}

// .NET 8
public int Min()
{
    return Enumerable.Min(_source);
}
// .NET 8
public int Max()
{
    return Enumerable.Max(_source);
}
// .NET 8
public double Average()
{
    return Enumerable.Average(_source);
}
// .NET 8
public int Sum()
{
    return Enumerable.Sum(_source);
}

// .NET 8
.method public hidebysig 
    instance int32 Min () cil managed 
{
    .custom instance void [BenchmarkDotNet.Annotations]BenchmarkDotNet.Attributes.BenchmarkAttribute::.ctor(int32, string) = (
        01 00 1f 00 00 00 01 5f 00 00
    )
    // Method begins at RVA 0x2075
    // Code size 12 (0xc)
    .maxstack 8

    // sequence point: (line 32, col 25) to (line 32, col 38) in _
    IL_0000: ldarg.0
    IL_0001: ldfld class [System.Runtime]System.Collections.Generic.IEnumerable`1<int32> Benchmarks.LinqAggregateBenchmark::_source
    IL_0006: call int32 [System.Linq]System.Linq.Enumerable::Min(class [System.Runtime]System.Collections.Generic.IEnumerable`1<int32>)
    IL_000b: ret
}
// .NET 8
.method public hidebysig 
    instance int32 Max () cil managed 
{
    .custom instance void [BenchmarkDotNet.Annotations]BenchmarkDotNet.Attributes.BenchmarkAttribute::.ctor(int32, string) = (
        01 00 22 00 00 00 01 5f 00 00
    )
    // Method begins at RVA 0x2082
    // Code size 12 (0xc)
    .maxstack 8

    // sequence point: (line 35, col 25) to (line 35, col 38) in _
    IL_0000: ldarg.0
    IL_0001: ldfld class [System.Runtime]System.Collections.Generic.IEnumerable`1<int32> Benchmarks.LinqAggregateBenchmark::_source
    IL_0006: call int32 [System.Linq]System.Linq.Enumerable::Max(class [System.Runtime]System.Collections.Generic.IEnumerable`1<int32>)
    IL_000b: ret
}
// .NET 8
.method public hidebysig 
    instance float64 Average () 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 0x208f
    // Code size 12 (0xc)
    .maxstack 8

    // sequence point: (line 38, col 32) to (line 38, col 49) in _
    IL_0000: ldarg.0
    IL_0001: ldfld class [System.Runtime]System.Collections.Generic.IEnumerable`1<int32> Benchmarks.LinqAggregateBenchmark::_source
    IL_0006: call float64 [System.Linq]System.Linq.Enumerable::Average(class [System.Runtime]System.Collections.Generic.IEnumerable`1<int32>)
    IL_000b: ret
}
// .NET 8
.method public hidebysig 
    instance int32 Sum () 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 0x209c
    // Code size 12 (0xc)
    .maxstack 8

    // sequence point: (line 41, col 25) to (line 41, col 38) in _
    IL_0000: ldarg.0
    IL_0001: ldfld class [System.Runtime]System.Collections.Generic.IEnumerable`1<int32> Benchmarks.LinqAggregateBenchmark::_source
    IL_0006: call int32 [System.Linq]System.Linq.Enumerable::Sum(class [System.Runtime]System.Collections.Generic.IEnumerable`1<int32>)
    IL_000b: ret
}

// .NET 8 (X64)
Min()
    L0000: mov rcx, [rcx+8]
    L0004: jmp qword ptr [0x7ffd3ea5e8b0]
// .NET 8 (X64)
Max()
    L0000: mov rcx, [rcx+8]
    L0004: jmp qword ptr [0x7ffd3ea5ff48]
// .NET 8 (X64)
Average()
    L0000: mov rcx, [rcx+8]
    L0004: jmp qword ptr [0x7ffd384f7150]
// .NET 8 (X64)
Sum()
    L0000: mov rcx, [rcx+8]
    L0004: jmp qword ptr [0x7ffd3ea5ff60]


Benchmark Description:


For both .NET 8 & 9 the benchmarks almost are the same, the slight point here is no matter what method you're using like Max or Sum, the memory allocation and speed are equal, the reason is the Linq and how it works! In all methods, Linq has to iterate on all items in the array.

Queued for ChatGPT description. Please try again in 1-3 minutes


Benchmark Comments: