Skip to content

Conversation

EgorBo
Copy link
Member

@EgorBo EgorBo commented Jul 8, 2025

Since I was not able to easily workaround the arg is address exposed caused by uint hashCode = (uint)key.GetHashCode(); line (which is ldarga + constrained. callvirt) which in its turns breaks the inliner-level constant propagation, let's see if this helps.
Also, remove string.EqualsOrdinalIgnoreCase - it was not unrolling friendly.

@EgorBo
Copy link
Member Author

EgorBo commented Jul 8, 2025

@MihuBot

@EgorBo
Copy link
Member Author

EgorBo commented Jul 8, 2025

Blocked on #117431

@EgorBo
Copy link
Member Author

EgorBo commented Jul 9, 2025

@EgorBot -amd -intel -arm

using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;

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

public class StringDictionaryDefault
{
    private static readonly Dictionary<string, int> StringDictionary = new()
    {
        { "DOTNET_ThreadPool_UnfairSemaphoreSpinLimit" + 1, 1 },
        { "Hello World" + 2, 2 },
        { "aa" + 3, 3 },
    };

    [Benchmark] public int LongKey() => StringDictionary["DOTNET_ThreadPool_UnfairSemaphoreSpinLimit1"];
    [Benchmark] public int MediumKey() => StringDictionary["Hello World2"];
    [Benchmark] public int ShortKey() => StringDictionary["aa3"];
}

public class StringDictionaryIgnoreCase
{
    private static readonly Dictionary<string, int> StringDictionary = new(StringComparer.OrdinalIgnoreCase)
    {
        { "DOTNET_ThreadPool_UnfairSemaphoreSpinLimit" + 1, 1 },
        { "Hello World" + 2, 2 },
        { "aa" + 3, 3 }
    };

    [Benchmark] public int LongKey_IgnoreCase() => StringDictionary["DOTNET_ThreadPool_UnfairSemaphoreSpinLimit1"];
    [Benchmark] public int MediumKey_IgnoreCase() => StringDictionary["Hello World2"];
    [Benchmark] public int ShortKey_IgnoreCase() => StringDictionary["aa3"];
}

public class StringDictionary_SameRef
{
    private static readonly Dictionary<string, int> StringDictionary = new()
    {
        { "DOTNET_ThreadPool_UnfairSemaphoreSpinLimit", 1 },
        { "Hello World", 2 },
        { "aa", 3 }
    };

    [Benchmark] public int LongKey_SameRef() => StringDictionary["DOTNET_ThreadPool_UnfairSemaphoreSpinLimit"];
    [Benchmark] public int MediumKey_SameRef() => StringDictionary["Hello World"];
    [Benchmark] public int ShortKey_SameRef() => StringDictionary["aa"];
}

@EgorBo EgorBo marked this pull request as ready for review July 9, 2025 01:17
@EgorBo
Copy link
Member Author

EgorBo commented Jul 9, 2025

PTAL @stephentoub essentially what this PR does - it enables unrolling for this Equals call (for both Ordinal and OrdinalIgnoreCase comparers) when Dynamic PGO decides to devirtualize those and the key is a string literal.

Benchmarks.

No impact on value-type keys.

@EgorBo
Copy link
Member Author

EgorBo commented Jul 16, 2025

/ba-g "android-x64 timed out"

@EgorBo EgorBo merged commit 02b1214 into dotnet:main Jul 16, 2025
141 of 144 checks passed
@EgorBo EgorBo deleted the improve-dictionary branch July 16, 2025 10:50
@github-actions github-actions bot locked and limited conversation to collaborators Aug 16, 2025
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