Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

stephentoub
Copy link
Member

The OrderedEnumerable implementation today doesn't derive from Iterator, and it has a GetEnumerator method implemented with yield. That means the optimization that allows the enumerable to be reused as the enumerator doesn't kick in, and we end up allocating a separate enumerator object in order to iterate through an Order{By} enumerable.

Method Toolchain Length Mean Ratio Allocated Alloc Ratio
Order \main\corerun.exe 2 51.61 ns 1.00 112 B 1.00
Order \pr\corerun.exe 2 46.16 ns 0.90 88 B 0.79
OrderBy \main\corerun.exe 2 86.11 ns 1.00 328 B 1.00
OrderBy \pr\corerun.exe 2 87.89 ns 1.02 304 B 0.93
Order \main\corerun.exe 100 652.72 ns 1.00 504 B 1.00
Order \pr\corerun.exe 100 580.34 ns 0.89 480 B 0.95
OrderBy \main\corerun.exe 100 1,982.54 ns 1.00 1504 B 1.00
OrderBy \pr\corerun.exe 100 1,989.22 ns 1.00 1480 B 0.98
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;

BenchmarkSwitcher.FromAssembly(typeof(Tests).Assembly).Run(args);

[MemoryDiagnoser(false)]
[HideColumns("Job", "Error", "StdDev", "Median", "RatioSD")]
public partial class Tests
{
    private int[] _ints;
    private Person[] _people;

    [Params(2, 100)]
    public int Length { get; set; }

    [GlobalSetup]
    public void Setup()
    {
        _ints = Enumerable.Range(0, Length).Reverse().ToArray();
        _people = _ints.Select(i => new Person { Age = i }).ToArray();
    }

    [Benchmark]
    public int Order()
    {
        int sum = 0;
        foreach (int i in _ints.Order()) sum += i;
        return sum;
    }

    [Benchmark]
    public int OrderBy()
    {
        int sum = 0;
        foreach (Person p in _people.OrderBy(p => p.Age)) sum += p.Age;
        return sum;
    }

    public struct Person
    {
        public int Age { get; set; }
    }
}

The OrderedEnumerable implementation today doesn't derive from Iterator, and it has a GetEnumerator method implemented with yield. That means the optimization that allows the enumerable to be reused as the enumerator doesn't kick in, and we end up allocating a separate enumerator object in order to iterate through an Order{By} enumerable.
@stephentoub stephentoub added this to the 9.0.0 milestone Feb 23, 2024
@ghost
Copy link

ghost commented Feb 23, 2024

Tagging subscribers to this area: @dotnet/area-system-linq
See info in area-owners.md if you want to be subscribed.

Issue Details

The OrderedEnumerable implementation today doesn't derive from Iterator, and it has a GetEnumerator method implemented with yield. That means the optimization that allows the enumerable to be reused as the enumerator doesn't kick in, and we end up allocating a separate enumerator object in order to iterate through an Order{By} enumerable.

Method Toolchain Length Mean Ratio Allocated Alloc Ratio
Order \main\corerun.exe 2 51.61 ns 1.00 112 B 1.00
Order \pr\corerun.exe 2 46.16 ns 0.90 88 B 0.79
OrderBy \main\corerun.exe 2 86.11 ns 1.00 328 B 1.00
OrderBy \pr\corerun.exe 2 87.89 ns 1.02 304 B 0.93
Order \main\corerun.exe 100 652.72 ns 1.00 504 B 1.00
Order \pr\corerun.exe 100 580.34 ns 0.89 480 B 0.95
OrderBy \main\corerun.exe 100 1,982.54 ns 1.00 1504 B 1.00
OrderBy \pr\corerun.exe 100 1,989.22 ns 1.00 1480 B 0.98
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;

BenchmarkSwitcher.FromAssembly(typeof(Tests).Assembly).Run(args);

[MemoryDiagnoser(false)]
[HideColumns("Job", "Error", "StdDev", "Median", "RatioSD")]
public partial class Tests
{
    private int[] _ints;
    private Person[] _people;

    [Params(2, 100)]
    public int Length { get; set; }

    [GlobalSetup]
    public void Setup()
    {
        _ints = Enumerable.Range(0, Length).Reverse().ToArray();
        _people = _ints.Select(i => new Person { Age = i }).ToArray();
    }

    [Benchmark]
    public int Order()
    {
        int sum = 0;
        foreach (int i in _ints.Order()) sum += i;
        return sum;
    }

    [Benchmark]
    public int OrderBy()
    {
        int sum = 0;
        foreach (Person p in _people.OrderBy(p => p.Age)) sum += p.Age;
        return sum;
    }

    public struct Person
    {
        public int Age { get; set; }
    }
}
Author: stephentoub
Assignees: -
Labels:

area-System.Linq

Milestone: 9.0.0

@ghost ghost assigned stephentoub Feb 23, 2024
@stephentoub stephentoub merged commit 99a7964 into dotnet:main Feb 24, 2024
@stephentoub stephentoub deleted the orderedenumerableiterator branch February 24, 2024 01:42
@github-actions github-actions bot locked and limited conversation to collaborators Mar 25, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants