Skip to content

Improve ComparerHelper.CreateDefaultEqualityComparer - #134321

Merged
tannergooding merged 3 commits into
dotnet:mainfrom
hanachiru:ComparerHelpers/CreateDefaultEqualityComparer
Sep 21, 2026
Merged

tannergooding merged 3 commits into
dotnet:mainfrom
hanachiru:ComparerHelpers/CreateDefaultEqualityComparer

Conversation

@hanachiru

Copy link
Copy Markdown
Contributor

Summary

This PR changes the nullable underlying type retrieval from GetGenericArguments() to GetNullableUnderlyingType() in ComparerHelpers.CreateDefaultEqualityComparer.
This change avoids allocating a generic argument array when creating an equality comparer for a closed Nullable<T> and uses the API introduced in #126905.

Benchmark

| Method                        | Job        | Toolchain | type                                | Mean       | Error      | StdDev     | Median     | Min        | Max        | Ratio | RatioSD | Gen0   | Allocated | Alloc Ratio |
|------------------------------ |----------- |---------- |------------------------------------ |-----------:|-----------:|-----------:|-----------:|-----------:|-----------:|------:|--------:|-------:|----------:|------------:|
| CreateDefaultEqualityComparer | Job-HCBSPO | /main     | System.Int32                        | 130.313 ns |  3.5073 ns |  4.0390 ns | 129.996 ns | 126.479 ns | 144.246 ns |  1.00 |    0.00 | 0.0104 |      88 B |        1.00 |
| CreateDefaultEqualityComparer | Job-QOJBTO | /pr       | System.Int32                        | 128.015 ns |  0.8196 ns |  0.7666 ns | 128.138 ns | 126.395 ns | 129.130 ns |  0.98 |    0.03 | 0.0103 |      88 B |        1.00 |
|                               |            |           |                                     |            |            |            |            |            |            |       |         |        |           |             |
| CreateDefaultEqualityComparer | Job-HCBSPO | /main     | System.Int64                        | 129.985 ns |  2.5982 ns |  2.7801 ns | 128.916 ns | 127.263 ns | 137.733 ns |  1.00 |    0.00 | 0.0103 |      88 B |        1.00 |
| CreateDefaultEqualityComparer | Job-QOJBTO | /pr       | System.Int64                        | 130.928 ns |  3.6140 ns |  4.1619 ns | 129.683 ns | 127.794 ns | 147.174 ns |  1.01 |    0.04 | 0.0104 |      88 B |        1.00 |
|                               |            |           |                                     |            |            |            |            |            |            |       |         |        |           |             |
| CreateDefaultEqualityComparer | Job-HCBSPO | /main     | System.Nullable`1[System.DateTime]  | 161.345 ns | 11.2124 ns | 12.9122 ns | 154.015 ns | 152.674 ns | 197.246 ns |  1.00 |    0.00 | 0.0141 |     120 B |        1.00 |
| CreateDefaultEqualityComparer | Job-QOJBTO | /pr       | System.Nullable`1[System.DateTime]  | 132.521 ns |  6.1224 ns |  7.0506 ns | 130.353 ns | 128.537 ns | 157.255 ns |  0.83 |    0.07 | 0.0104 |      88 B |        0.73 |
|                               |            |           |                                     |            |            |            |            |            |            |       |         |        |           |             |
| CreateDefaultEqualityComparer | Job-HCBSPO | /main     | System.Nullable`1[System.DayOfWeek] | 153.169 ns |  1.5292 ns |  1.4304 ns | 153.055 ns | 151.425 ns | 157.249 ns |  1.00 |    0.00 | 0.0140 |     120 B |        1.00 |
| CreateDefaultEqualityComparer | Job-QOJBTO | /pr       | System.Nullable`1[System.DayOfWeek] | 127.887 ns |  0.4688 ns |  0.4385 ns | 127.849 ns | 127.047 ns | 128.805 ns |  0.84 |    0.01 | 0.0104 |      88 B |        0.73 |
|                               |            |           |                                     |            |            |            |            |            |            |       |         |        |           |             |
| CreateDefaultEqualityComparer | Job-HCBSPO | /main     | System.Nullable`1[System.Int32]     | 156.321 ns |  1.7346 ns |  1.6225 ns | 155.847 ns | 154.560 ns | 160.061 ns |  1.00 |    0.00 | 0.0137 |     120 B |        1.00 |
| CreateDefaultEqualityComparer | Job-QOJBTO | /pr       | System.Nullable`1[System.Int32]     | 130.704 ns |  1.0851 ns |  1.0150 ns | 130.424 ns | 129.437 ns | 133.321 ns |  0.84 |    0.01 | 0.0103 |      88 B |        0.73 |
|                               |            |           |                                     |            |            |            |            |            |            |       |         |        |           |             |
| CreateDefaultEqualityComparer | Job-HCBSPO | /main     | System.Nullable`1[System.Int64]     | 160.051 ns |  9.1930 ns | 10.5867 ns | 155.247 ns | 152.374 ns | 192.656 ns |  1.00 |    0.00 | 0.0137 |     120 B |        1.00 |
| CreateDefaultEqualityComparer | Job-QOJBTO | /pr       | System.Nullable`1[System.Int64]     | 131.059 ns |  2.7503 ns |  3.1673 ns | 130.331 ns | 127.530 ns | 137.181 ns |  0.82 |    0.05 | 0.0102 |      88 B |        0.73 |
|                               |            |           |                                     |            |            |            |            |            |            |       |         |        |           |             |
| CreateDefaultEqualityComparer | Job-HCBSPO | /main     | System.String                       |   3.759 ns |  0.1103 ns |  0.1270 ns |   3.779 ns |   3.500 ns |   4.077 ns |  1.00 |    0.00 | 0.0029 |      24 B |        1.00 |
| CreateDefaultEqualityComparer | Job-QOJBTO | /pr       | System.String                       |   3.800 ns |  0.0976 ns |  0.1124 ns |   3.781 ns |   3.637 ns |   4.148 ns |  1.01 |    0.04 | 0.0029 |      24 B |        1.00 |
[BenchmarkCategory(Categories.Libraries)]
public class Perf_EqualityComparer
{
    private static readonly Func<Type, object> s_createDefaultEqualityComparer =
        (Func<Type, object>)typeof(EqualityComparer<>).Assembly
            .GetType("System.Collections.Generic.ComparerHelpers", throwOnError: true)!
            .GetMethod(
                "CreateDefaultEqualityComparer",
                BindingFlags.NonPublic | BindingFlags.Static)!
            .CreateDelegate(typeof(Func<Type, object>));

    public static IEnumerable<Type> Arguments()
    { 
        yield return typeof(int);
        yield return typeof(long);
        yield return typeof(string);
        yield return typeof(int?);
        yield return typeof(long?);
        yield return typeof(DateTime?);
        yield return typeof(DayOfWeek?);
    }

    [Benchmark]
    [ArgumentsSource(nameof(Arguments))]
    [MemoryRandomization]
    public object CreateDefaultEqualityComparer(Type type)
    {
        return s_createDefaultEqualityComparer(type);
    }
}

@dotnet-policy-service dotnet-policy-service Bot added the community-contribution Indicates that the PR has been added by a community member label Sep 21, 2026
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 3 pipeline(s).
13 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

@dotnet-policy-service

Copy link
Copy Markdown
Contributor

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

@jkotas jkotas left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks

@adamsitnik adamsitnik added the tenet-performance Performance related issue label Sep 21, 2026
@adamsitnik adamsitnik added this to the 12.0.0 milestone Sep 21, 2026
@tannergooding
tannergooding enabled auto-merge (squash) September 21, 2026 13:56
@tannergooding
tannergooding merged commit 5014aac into dotnet:main Sep 21, 2026
115 of 117 checks passed
@hanachiru
hanachiru deleted the ComparerHelpers/CreateDefaultEqualityComparer branch September 22, 2026 11:45
tannergooding pushed a commit that referenced this pull request Sep 22, 2026
# Summary

This PR changes the nullable underlying type retrieval from
`GetGenericArguments()` to `GetNullableUnderlyingType()` in
`ComparerHelpers.CreateDefaultComparer`.

This change avoids allocating a generic argument array when creating an
equality comparer for a closed `Nullable<T>` and uses the API introduced
in [#126905](#126905).

This is a follow-up to
[#134321](#134321), which applied
the same change to the sibling method
`ComparerHelpers.CreateDefaultEqualityComparer`. Both methods in this
file now use the same idiom.

# Benchmark
```
| Method                | Job        | Toolchain | type                                | Mean     | Error   | StdDev  | Median   | Min      | Max      | Ratio | Gen0   | Allocated | Alloc Ratio |
|---------------------- |----------- |---------- |------------------------------------ |---------:|--------:|--------:|---------:|---------:|---------:|------:|-------:|----------:|------------:|
| CreateDefaultComparer | Job-HCBSPO | /main     | System.Int32                        | 127.6 ns | 0.98 ns | 0.92 ns | 127.7 ns | 126.4 ns | 130.4 ns |  1.00 | 0.0103 |      88 B |        1.00 |
| CreateDefaultComparer | Job-QOJBTO | /pr       | System.Int32                        | 126.6 ns | 1.44 ns | 1.35 ns | 126.3 ns | 126.0 ns | 131.4 ns |  0.99 | 0.0102 |      88 B |        1.00 |
|                       |            |           |                                     |          |         |         |          |          |          |       |        |           |             |
| CreateDefaultComparer | Job-HCBSPO | /main     | System.Int64                        | 129.1 ns | 1.30 ns | 1.21 ns | 128.7 ns | 128.4 ns | 133.3 ns |  1.00 | 0.0104 |      88 B |        1.00 |
| CreateDefaultComparer | Job-QOJBTO | /pr       | System.Int64                        | 127.6 ns | 1.46 ns | 1.37 ns | 127.2 ns | 126.4 ns | 132.1 ns |  0.99 | 0.0101 |      88 B |        1.00 |
|                       |            |           |                                     |          |         |         |          |          |          |       |        |           |             |
| CreateDefaultComparer | Job-HCBSPO | /main     | System.Nullable`1[System.DateTime]  | 154.3 ns | 1.79 ns | 1.68 ns | 153.9 ns | 152.3 ns | 159.1 ns |  1.00 | 0.0138 |     120 B |        1.00 |
| CreateDefaultComparer | Job-QOJBTO | /pr       | System.Nullable`1[System.DateTime]  | 127.4 ns | 0.43 ns | 0.40 ns | 127.4 ns | 126.8 ns | 128.2 ns |  0.83 | 0.0102 |      88 B |        0.73 |
|                       |            |           |                                     |          |         |         |          |          |          |       |        |           |             |
| CreateDefaultComparer | Job-HCBSPO | /main     | System.Nullable`1[System.DayOfWeek] | 155.0 ns | 1.48 ns | 1.38 ns | 154.7 ns | 154.1 ns | 159.9 ns |  1.00 | 0.0143 |     120 B |        1.00 |
| CreateDefaultComparer | Job-QOJBTO | /pr       | System.Nullable`1[System.DayOfWeek] | 127.8 ns | 0.38 ns | 0.35 ns | 127.9 ns | 127.3 ns | 128.3 ns |  0.82 | 0.0102 |      88 B |        0.73 |
|                       |            |           |                                     |          |         |         |          |          |          |       |        |           |             |
| CreateDefaultComparer | Job-HCBSPO | /main     | System.Nullable`1[System.Int32]     | 154.9 ns | 1.49 ns | 1.40 ns | 154.4 ns | 153.8 ns | 159.6 ns |  1.00 | 0.0141 |     120 B |        1.00 |
| CreateDefaultComparer | Job-QOJBTO | /pr       | System.Nullable`1[System.Int32]     | 126.9 ns | 1.14 ns | 1.07 ns | 126.6 ns | 126.3 ns | 130.6 ns |  0.82 | 0.0102 |      88 B |        0.73 |
|                       |            |           |                                     |          |         |         |          |          |          |       |        |           |             |
| CreateDefaultComparer | Job-HCBSPO | /main     | System.Nullable`1[System.Int64]     | 155.5 ns | 1.72 ns | 1.61 ns | 155.1 ns | 154.7 ns | 161.3 ns |  1.00 | 0.0142 |     120 B |        1.00 |
| CreateDefaultComparer | Job-QOJBTO | /pr       | System.Nullable`1[System.Int64]     | 127.7 ns | 1.55 ns | 1.45 ns | 127.4 ns | 126.8 ns | 132.9 ns |  0.82 | 0.0101 |      88 B |        0.73 |
|                       |            |           |                                     |          |         |         |          |          |          |       |        |           |             |
| CreateDefaultComparer | Job-HCBSPO | /main     | System.String                       | 128.3 ns | 1.54 ns | 1.44 ns | 127.9 ns | 127.5 ns | 133.4 ns |  1.00 | 0.0102 |      88 B |        1.00 |
| CreateDefaultComparer | Job-QOJBTO | /pr       | System.String                       | 128.1 ns | 1.33 ns | 1.25 ns | 127.8 ns | 127.4 ns | 132.6 ns |  1.00 | 0.0102 |      88 B |        1.00 |
```

```cs
[BenchmarkCategory(Categories.Libraries)]
public class Perf_ComparerHelpers
{
    private static readonly Func<Type, object> s_createDefaultComparer =
        (Func<Type, object>)typeof(Comparer<>).Assembly
            .GetType("System.Collections.Generic.ComparerHelpers", throwOnError: true)!
            .GetMethod(
                "CreateDefaultComparer",
                BindingFlags.NonPublic | BindingFlags.Static)!
            .CreateDelegate(typeof(Func<Type, object>));

    public static IEnumerable<Type> Arguments()
    { 
        yield return typeof(int);
        yield return typeof(long);
        yield return typeof(string);
        yield return typeof(int?);
        yield return typeof(long?);
        yield return typeof(DateTime?);
        yield return typeof(DayOfWeek?);
    }

    [Benchmark]
    [ArgumentsSource(nameof(Arguments))]
    [MemoryRandomization]
    public object CreateDefaultComparer(Type type)
    {
        return s_createDefaultComparer(type);
    }
}
```
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-System.Memory community-contribution Indicates that the PR has been added by a community member tenet-performance Performance related issue

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants