Skip to content

Optimize Queue<T>.Enumerator #117341

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open

Conversation

stephentoub
Copy link
Member

@stephentoub stephentoub commented Jul 6, 2025

Related to #117328

Method Toolchain Wraps Length Mean Ratio Allocated
IntsEnumerable \main\corerun.exe False 3 18.187 ns 1.00 -
IntsEnumerable \pr\corerun.exe False 3 4.249 ns 0.23 -
IntsQueue \main\corerun.exe False 3 9.710 ns 1.00 -
IntsQueue \pr\corerun.exe False 3 3.933 ns 0.41 -
StringsEnumerable \main\corerun.exe False 3 19.448 ns 1.00 -
StringsEnumerable \pr\corerun.exe False 3 4.574 ns 0.24 -
StringsQueue \main\corerun.exe False 3 9.564 ns 1.00 -
StringsQueue \pr\corerun.exe False 3 3.026 ns 0.32 -
IntsEnumerable \main\corerun.exe False 100 212.356 ns 1.00 40 B
IntsEnumerable \pr\corerun.exe False 100 119.884 ns 0.56 -
IntsQueue \main\corerun.exe False 100 260.983 ns 1.00 -
IntsQueue \pr\corerun.exe False 100 76.163 ns 0.29 -
StringsEnumerable \main\corerun.exe False 100 363.271 ns 1.00 40 B
StringsEnumerable \pr\corerun.exe False 100 126.384 ns 0.35 -
StringsQueue \main\corerun.exe False 100 250.484 ns 1.00 -
StringsQueue \pr\corerun.exe False 100 76.951 ns 0.31 -
IntsEnumerable \main\corerun.exe True 3 16.776 ns 1.00 40 B
IntsEnumerable \pr\corerun.exe True 3 5.312 ns 0.32 -
IntsQueue \main\corerun.exe True 3 9.567 ns 1.00 -
IntsQueue \pr\corerun.exe True 3 2.911 ns 0.30 -
StringsEnumerable \main\corerun.exe True 3 20.782 ns 1.00 -
StringsEnumerable \pr\corerun.exe True 3 3.919 ns 0.19 -
StringsQueue \main\corerun.exe True 3 14.296 ns 1.00 -
StringsQueue \pr\corerun.exe True 3 3.835 ns 0.27 -
IntsEnumerable \main\corerun.exe True 100 271.858 ns 1.00 40 B
IntsEnumerable \pr\corerun.exe True 100 99.918 ns 0.37 -
IntsQueue \main\corerun.exe True 100 245.360 ns 1.00 -
IntsQueue \pr\corerun.exe True 100 82.029 ns 0.33 -
StringsEnumerable \main\corerun.exe True 100 361.511 ns 1.00 40 B
StringsEnumerable \pr\corerun.exe True 100 101.535 ns 0.28 -
StringsQueue \main\corerun.exe True 100 253.267 ns 1.00 -
StringsQueue \pr\corerun.exe True 100 83.072 ns 0.33 -
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;

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

[MemoryDiagnoser(false)]
public class Bench
{
    private IEnumerable<int> _intsEnumerable;
    private Queue<int> _intsQueue;
    private IEnumerable<string> _stringsEnumerable;
    private Queue<string> _stringsQueue;

    [Params(false, true)]
    public bool Wraps { get; set; }

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

    [GlobalSetup]
    public void Setup()
    {
        _intsQueue = new Queue<int>(Length);
        _stringsQueue = new Queue<string>(Length);

        for (int i = 0; i < Length; i++)
        {
            _intsQueue.Enqueue(i);
            _stringsQueue.Enqueue(i.ToString());
        }

        if (Wraps)
        {
            for (int i = 0; i < Length - 1; i++)
            {
                _intsQueue.Dequeue();
                _intsQueue.Enqueue(i + Length);

                _stringsQueue.Dequeue();
                _stringsQueue.Enqueue((i + Length).ToString());
            }
        }

        _intsEnumerable = _intsQueue;
        _stringsEnumerable = _stringsQueue;
    }

    [Benchmark]
    public int IntsEnumerable()
    {
        int sum = 0;
        foreach (var i in _intsEnumerable) sum += i;
        return sum;
    }

    [Benchmark]
    public int IntsQueue()
    {
        int sum = 0;
        foreach (var i in _intsQueue) sum += i;
        return sum;
    }

    [Benchmark]
    public int StringsEnumerable()
    {
        int sum = 0;
        foreach (var s in _stringsEnumerable) sum += s.Length;
        return sum;
    }

    [Benchmark]
    public int StringsQueue()
    {
        int sum = 0;
        foreach (var s in _stringsQueue) sum += s.Length;
        return sum;
    }
}

@Copilot Copilot AI review requested due to automatic review settings July 6, 2025 02:06
@github-actions github-actions bot added the needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners label Jul 6, 2025
Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

Optimizes Queue<T>.Enumerator by rewriting internal fields and the MoveNext loop for performance, and updates related test properties to reflect changed behavior.

  • Renames internal enumerator fields (_q_queue, _index_i) and refactors MoveNext to eliminate expensive modulo operations.
  • Updates Current and Reset implementations accordingly.
  • Renames test properties in both generic and non-generic Queue tests to include “Empty” in the undefined-operation checks.

Reviewed Changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
src/libraries/System.Private.CoreLib/src/System/Collections/Generic/Queue.cs Refactored enumerator internals, field renames, and loop logic
src/libraries/System.Collections/tests/Generic/Queue/Queue.Tests.cs Renamed test property for undefined Current operation
src/libraries/System.Collections/tests/Generic/Queue/Queue.Generic.Tests.cs Renamed generic test property for undefined Current operation
Comments suppressed due to low confidence (2)

src/libraries/System.Collections/tests/Generic/Queue/Queue.Tests.cs:30

  • [nitpick] The renamed test property Enumerator_Empty_Current_UndefinedOperation_Throw is inconsistent with its original plural suffix and the naming in base classes. Rename it to Enumerator_Empty_Current_UndefinedOperation_Throws to match the established pattern.
        protected override bool Enumerator_Empty_Current_UndefinedOperation_Throw => true;

src/libraries/System.Collections/tests/Generic/Queue/Queue.Generic.Tests.cs:54

  • [nitpick] Ensure the test property naming matches across both generic and non-generic fixtures; if Enumerator_Empty_Current_UndefinedOperation_Throws is intended here, update the non-generic test to use the same name for consistency.
        protected override bool Enumerator_Empty_Current_UndefinedOperation_Throws => true;

@stephentoub stephentoub added area-System.Collections and removed needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners labels Jul 6, 2025
Copy link
Contributor

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants