Skip to content
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

Avoid generic virtual dispatch for frozen collections alternate lookup #108732

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

Conversation

andrewjsaid
Copy link
Contributor

Background

In .NET 9 the Alternate Lookup concept was added to dictionaries where you could look up in a dictionary by a key type different than the original one. The main motivator was reading data off the wire e.g. keying a Dictionary<string, T> using a ReadOnlySpan<char> (or byte). Thus I created a PR in ASP.NET Core to use this new method on FrozenDictionary<string, T> when matching routing but was surprised to find that there's quite a hefty penalty when using Alternate Lookups due to the generic virtual dispatch mechanism itself being a dictionary. Thus while the memory allocations disappeared the performance in terms of speed improved only slightly in the microbenchmarks.

See dotnet/aspnetcore#58305

Solution

FrozenDictionary<string, T> currently exposes a method with the following signature where the actual work happens.

private protected virtual ref readonly TValue GetValueRefOrNullRefCore<TAlternateKey>(TAlternateKey key)

To replace it, a new mechanism was added:

private protected virtual AlternateLookupDelegate<TAlternateKey> GetAlternateLookupDelegate<TAlternateKey>()

internal delegate ref readonly TValue AlternateLookupDelegate<TAlternateKey>(FrozenDictionary<TKey, TValue> dictionary, TAlternateKey key);

The idea is that when the AlternateLookup struct is created it pays the price of the generic virtual dispatch once. It can then use the delegate as follows to avoid generic virtual dispatch:

ref readonly TValue valueRef = ref _alternateLookupDelegate(Dictionary, key);

In concrete types of FrozenDictionary we can then use the following pattern to cache a static instance of the delegate which calls the actual worker method.

// pseudocode - some details removed

private protected override AlternateLookupDelegate<TAlternateKey> GetAlternateLookupDelegate<TAlternateKey>()
    => AlternateKeyDelegateHolder<TAlternateKey>.Instance;

private static class AlternateKeyDelegateHolder<TAlternateKey>
{
    public static AlternateLookupDelegate<TAlternateKey> Instance = (dictionary, key)
        => ref ((DefaultFrozenDictionary<TKey, TValue>)dictionary).GetValueRefOrNullRefCoreAlternate(key);
}

private ref readonly TValue GetValueRefOrNullRefCoreAlternate<TAlternateKey>(TAlternateKey key)
{
    ...
}

In the above example GetAlternateLookupDelegate<TAlternateKey> simply loads a singleton delegate from a nested generic type. The delegate is implemented by calling GetValueRefOrNullRefCoreAlternate<TAlternateKey which although generic importantly is not a virtual method.

In order to avoid repeating this pattern for all the classes inheriting from OrdinalStringFrozenDictionary, a different pattern is used there. Note that in this case we do not need generics as we know that the concrete type of TAlternateKey will be ReadOnlySpan<char>, a fact which is asserted when creating the lookup.

private static AlternateLookupDelegate<ReadOnlySpan<char>> s_alternateLookup = (dictionary, key)
    => ref ((OrdinalStringFrozenDictionary<TValue>)dictionary).GetValueRefOrNullRefCoreAlternate(key);

private protected override AlternateLookupDelegate<TAlternateKey> GetAlternateLookupDelegate<TAlternateKey>()
{
    Debug.Assert(typeof(TAlternateKey) == typeof(ReadOnlySpan<char>));
    return (AlternateLookupDelegate<TAlternateKey>)(object)s_alternateLookup;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private protected virtual ref readonly TValue GetValueRefOrNullRefCoreAlternate(ReadOnlySpan<char> key)
{
    ... existing code
}

In the above example we are able to remove the generics from the worker method, and thus we can make it virtual - still avoiding the combination of generics and virtual dispatch. Thus the derived types can use the existing approach as follows.

private protected override ref readonly TValue GetValueRefOrNullRefCoreAlternate(ReadOnlySpan<char> key) => ref base.GetValueRefOrNullRefCoreAlternate(key);

Note that further investigation is necessary to measure whether it is a good idea to remove the virtual dispatch entirely even for classes inheriting OrdinalStringFrozenDictionary. It would result in more complex code but could be faster depending on how expensive the (non-generic) virtual dispatch is.

Conclusion

As we can see from the benchmarks below, the PR does not change the "normal" TryGetValue scenario and improves the "Alternate Lookup" TryGetValue scenario significantly.

This way the alternate lookup still suffers a performance penalty but it's not quite as significant and is more likely to justify it's use if it results in fewer allocations.

Benchmark Results

dotnet/performance Frozen Dictionary benchmarks (https://github.com/dotnet/performance/blob/main/src/benchmarks/micro/libraries/System.Collections/Frozen/Perf_FrozenDictionary_String.cs) were modified locally to add a case to use the AlternateLookup feature. I didn't create a PR as I couldn't find a clean way to add it with conditional compilation for .NET 9+

DefaultFrozenDictionary
Method Toolchain Count Mean Error StdDev Median Min Max Ratio RatioSD Allocated Alloc Ratio
TryGetValue_True_FrozenDictionary main 10 24.82 ns 0.233 ns 0.218 ns 24.74 ns 24.45 ns 25.16 ns 1.00 0.01 - NA
TryGetValue_True_FrozenDictionary pr 10 24.93 ns 0.249 ns 0.233 ns 24.91 ns 24.65 ns 25.35 ns 1.00 0.01 - NA
TryGetValue_False_FrozenDictionary main 10 23.37 ns 0.207 ns 0.184 ns 23.30 ns 23.16 ns 23.81 ns 1.00 0.01 - NA
TryGetValue_False_FrozenDictionary pr 10 22.61 ns 0.174 ns 0.154 ns 22.55 ns 22.47 ns 22.93 ns 0.97 0.01 - NA
TryGetValue_True_FrozenDictionaryLookup main 10 141.36 ns 0.659 ns 0.584 ns 141.34 ns 140.08 ns 142.49 ns 1.00 0.01 - NA
TryGetValue_True_FrozenDictionaryLookup pr 10 62.76 ns 0.461 ns 0.409 ns 62.84 ns 61.81 ns 63.13 ns 0.44 0.00 - NA
TryGetValue_False_FrozenDictionaryLookup main 10 121.20 ns 1.008 ns 0.943 ns 121.08 ns 119.80 ns 122.92 ns 1.00 0.01 - NA
TryGetValue_False_FrozenDictionaryLookup pr 10 46.76 ns 0.294 ns 0.261 ns 46.65 ns 46.43 ns 47.30 ns 0.39 0.00 - NA
TryGetValue_True_FrozenDictionary main 100 835.82 ns 9.389 ns 8.783 ns 839.17 ns 821.16 ns 851.62 ns 1.00 0.01 - NA
TryGetValue_True_FrozenDictionary pr 100 812.22 ns 8.532 ns 7.563 ns 815.04 ns 800.04 ns 822.71 ns 0.97 0.01 - NA
TryGetValue_False_FrozenDictionary main 100 566.00 ns 1.487 ns 1.161 ns 565.81 ns 564.35 ns 569.07 ns 1.00 0.00 - NA
TryGetValue_False_FrozenDictionary pr 100 565.69 ns 3.029 ns 2.833 ns 564.75 ns 562.08 ns 571.06 ns 1.00 0.01 - NA
TryGetValue_True_FrozenDictionaryLookup main 100 2,006.62 ns 13.648 ns 12.767 ns 2,000.68 ns 1,990.83 ns 2,024.54 ns 1.00 0.01 - NA
TryGetValue_True_FrozenDictionaryLookup pr 100 1,196.25 ns 7.592 ns 7.102 ns 1,193.14 ns 1,190.27 ns 1,211.04 ns 0.60 0.01 - NA
TryGetValue_False_FrozenDictionaryLookup main 100 1,467.58 ns 13.478 ns 12.607 ns 1,467.24 ns 1,447.26 ns 1,491.49 ns 1.00 0.01 - NA
TryGetValue_False_FrozenDictionaryLookup pr 100 809.94 ns 4.464 ns 4.176 ns 811.04 ns 802.96 ns 815.78 ns 0.55 0.01 - NA
TryGetValue_True_FrozenDictionary main 1000 8,698.64 ns 42.144 ns 39.422 ns 8,711.26 ns 8,636.79 ns 8,771.65 ns 1.00 0.01 - NA
TryGetValue_True_FrozenDictionary pr 1000 8,634.08 ns 29.948 ns 26.548 ns 8,631.32 ns 8,574.59 ns 8,669.79 ns 0.99 0.01 - NA
TryGetValue_False_FrozenDictionary main 1000 7,982.33 ns 67.440 ns 63.083 ns 8,020.97 ns 7,910.93 ns 8,060.76 ns 1.00 0.01 - NA
TryGetValue_False_FrozenDictionary pr 1000 8,026.40 ns 11.676 ns 9.750 ns 8,024.86 ns 8,012.28 ns 8,051.13 ns 1.01 0.01 - NA
TryGetValue_True_FrozenDictionaryLookup main 1000 19,852.27 ns 150.477 ns 140.757 ns 19,923.25 ns 19,617.70 ns 20,001.71 ns 1.00 0.01 - NA
TryGetValue_True_FrozenDictionaryLookup pr 1000 12,404.54 ns 31.612 ns 29.570 ns 12,407.62 ns 12,340.32 ns 12,449.85 ns 0.62 0.00 - NA
TryGetValue_False_FrozenDictionaryLookup main 1000 16,470.71 ns 77.600 ns 72.587 ns 16,491.01 ns 16,215.28 ns 16,518.22 ns 1.00 0.01 - NA
TryGetValue_False_FrozenDictionaryLookup pr 1000 10,038.52 ns 56.170 ns 52.541 ns 10,056.95 ns 9,911.11 ns 10,084.57 ns 0.61 0.00 - NA
TryGetValue_True_FrozenDictionary main 10000 173,394.89 ns 765.735 ns 639.423 ns 173,490.45 ns 171,768.40 ns 174,356.74 ns 1.00 0.01 - NA
TryGetValue_True_FrozenDictionary pr 10000 168,422.47 ns 2,242.735 ns 2,097.856 ns 167,781.79 ns 165,855.58 ns 172,569.42 ns 0.97 0.01 - NA
TryGetValue_False_FrozenDictionary main 10000 138,800.01 ns 995.304 ns 931.008 ns 138,332.41 ns 137,750.06 ns 140,323.01 ns 1.00 0.01 - NA
TryGetValue_False_FrozenDictionary pr 10000 142,408.54 ns 1,163.682 ns 1,031.574 ns 142,248.41 ns 140,787.56 ns 144,191.36 ns 1.03 0.01 - NA
TryGetValue_True_FrozenDictionaryLookup main 10000 330,175.06 ns 5,147.664 ns 4,815.128 ns 332,063.56 ns 324,248.27 ns 338,772.74 ns 1.00 0.02 1 B 1.00
TryGetValue_True_FrozenDictionaryLookup pr 10000 223,406.27 ns 2,984.179 ns 2,791.403 ns 224,066.34 ns 218,652.50 ns 227,634.64 ns 0.68 0.01 1 B 1.00
TryGetValue_False_FrozenDictionaryLookup main 10000 233,352.88 ns 950.977 ns 742.460 ns 233,392.26 ns 232,108.02 ns 234,459.24 ns 1.00 0.00 1 B 1.00
TryGetValue_False_FrozenDictionaryLookup pr 10000 172,137.36 ns 1,150.259 ns 960.519 ns 172,313.38 ns 170,510.33 ns 173,503.94 ns 0.74 0.00 - 0.00
LengthBucketsFrozenDictionary
Method Toolchain Count ItemsPerBucket Mean Error StdDev Median Min Max Ratio RatioSD Allocated Alloc Ratio
TryGetValue_True_FrozenDictionary main 10 1 19.902 ns 0.1293 ns 0.1080 ns 19.914 ns 19.732 ns 20.044 ns 1.00 0.01 - NA
TryGetValue_True_FrozenDictionary pr 10 1 20.099 ns 0.1980 ns 0.1852 ns 20.111 ns 19.815 ns 20.473 ns 1.01 0.01 - NA
TryGetValue_False_FrozenDictionary main 10 1 8.641 ns 0.1863 ns 0.1742 ns 8.630 ns 8.381 ns 9.011 ns 1.00 0.03 - NA
TryGetValue_False_FrozenDictionary pr 10 1 8.827 ns 0.1284 ns 0.1138 ns 8.823 ns 8.648 ns 9.015 ns 1.02 0.02 - NA
TryGetValue_True_FrozenDictionaryLookup main 10 1 142.569 ns 0.8472 ns 0.7924 ns 142.567 ns 141.324 ns 144.147 ns 1.00 0.01 - NA
TryGetValue_True_FrozenDictionaryLookup pr 10 1 50.636 ns 0.3343 ns 0.3127 ns 50.754 ns 50.135 ns 51.069 ns 0.36 0.00 - NA
TryGetValue_False_FrozenDictionaryLookup main 10 1 105.402 ns 0.7166 ns 0.6352 ns 105.667 ns 104.272 ns 106.178 ns 1.00 0.01 - NA
TryGetValue_False_FrozenDictionaryLookup pr 10 1 18.745 ns 0.1785 ns 0.1670 ns 18.683 ns 18.514 ns 19.066 ns 0.18 0.00 - NA
TryGetValue_True_FrozenDictionary main 10 5 75.388 ns 0.4839 ns 0.4527 ns 75.218 ns 74.953 ns 76.199 ns 1.00 0.01 - NA
TryGetValue_True_FrozenDictionary pr 10 5 75.815 ns 0.6499 ns 0.6079 ns 76.034 ns 74.771 ns 76.590 ns 1.01 0.01 - NA
TryGetValue_False_FrozenDictionary main 10 5 8.964 ns 0.0512 ns 0.0478 ns 8.966 ns 8.873 ns 9.049 ns 1.00 0.01 - NA
TryGetValue_False_FrozenDictionary pr 10 5 8.990 ns 0.0672 ns 0.0596 ns 8.986 ns 8.867 ns 9.106 ns 1.00 0.01 - NA
TryGetValue_True_FrozenDictionaryLookup main 10 5 198.743 ns 1.6434 ns 1.3723 ns 199.309 ns 196.262 ns 199.941 ns 1.00 0.01 - NA
TryGetValue_True_FrozenDictionaryLookup pr 10 5 114.005 ns 0.7425 ns 0.6582 ns 113.873 ns 113.278 ns 115.215 ns 0.57 0.01 - NA
TryGetValue_False_FrozenDictionaryLookup main 10 5 105.027 ns 0.7483 ns 0.7000 ns 105.267 ns 103.690 ns 105.595 ns 1.00 0.01 - NA
TryGetValue_False_FrozenDictionaryLookup pr 10 5 18.512 ns 0.2034 ns 0.1803 ns 18.572 ns 18.258 ns 18.764 ns 0.18 0.00 - NA
TryGetValue_True_FrozenDictionary main 100 1 190.921 ns 1.2957 ns 1.2120 ns 190.475 ns 189.531 ns 192.608 ns 1.00 0.01 - NA
TryGetValue_True_FrozenDictionary pr 100 1 191.816 ns 1.2793 ns 1.1341 ns 192.120 ns 189.424 ns 193.094 ns 1.00 0.01 - NA
TryGetValue_False_FrozenDictionary main 100 1 72.753 ns 0.5778 ns 0.5405 ns 72.924 ns 71.989 ns 73.566 ns 1.00 0.01 - NA
TryGetValue_False_FrozenDictionary pr 100 1 73.241 ns 0.5564 ns 0.4932 ns 73.311 ns 72.368 ns 73.965 ns 1.01 0.01 - NA
TryGetValue_True_FrozenDictionaryLookup main 100 1 1,421.168 ns 13.9540 ns 11.6522 ns 1,419.412 ns 1,402.696 ns 1,449.842 ns 1.00 0.01 - NA
TryGetValue_True_FrozenDictionaryLookup pr 100 1 494.229 ns 5.8461 ns 5.4685 ns 496.437 ns 481.493 ns 499.093 ns 0.35 0.00 - NA
TryGetValue_False_FrozenDictionaryLookup main 100 1 1,041.986 ns 6.3858 ns 5.6609 ns 1,042.127 ns 1,034.252 ns 1,055.021 ns 1.00 0.01 - NA
TryGetValue_False_FrozenDictionaryLookup pr 100 1 179.455 ns 1.1414 ns 0.9531 ns 179.811 ns 177.226 ns 180.135 ns 0.17 0.00 - NA
TryGetValue_True_FrozenDictionary main 100 5 773.623 ns 5.2743 ns 4.9336 ns 775.966 ns 763.302 ns 778.488 ns 1.00 0.01 - NA
TryGetValue_True_FrozenDictionary pr 100 5 772.435 ns 4.6678 ns 4.1379 ns 773.435 ns 760.933 ns 775.983 ns 1.00 0.01 - NA
TryGetValue_False_FrozenDictionary main 100 5 73.001 ns 0.3449 ns 0.3226 ns 73.078 ns 72.371 ns 73.569 ns 1.00 0.01 - NA
TryGetValue_False_FrozenDictionary pr 100 5 72.374 ns 0.3982 ns 0.3325 ns 72.306 ns 71.948 ns 73.207 ns 0.99 0.01 - NA
TryGetValue_True_FrozenDictionaryLookup main 100 5 1,987.912 ns 17.1403 ns 16.0331 ns 1,995.164 ns 1,956.139 ns 2,005.592 ns 1.00 0.01 - NA
TryGetValue_True_FrozenDictionaryLookup pr 100 5 1,137.256 ns 11.9771 ns 11.2034 ns 1,134.633 ns 1,123.508 ns 1,156.275 ns 0.57 0.01 - NA
TryGetValue_False_FrozenDictionaryLookup main 100 5 1,040.490 ns 8.2041 ns 7.6741 ns 1,042.214 ns 1,028.159 ns 1,052.716 ns 1.00 0.01 - NA
TryGetValue_False_FrozenDictionaryLookup pr 100 5 178.391 ns 1.5918 ns 1.4890 ns 177.725 ns 176.828 ns 181.733 ns 0.17 0.00 - NA
TryGetValue_True_FrozenDictionary main 1000 1 2,142.619 ns 9.0847 ns 7.0928 ns 2,141.511 ns 2,134.290 ns 2,159.042 ns 1.00 0.00 - NA
TryGetValue_True_FrozenDictionary pr 1000 1 2,124.494 ns 17.4370 ns 15.4574 ns 2,118.313 ns 2,108.085 ns 2,157.025 ns 0.99 0.01 - NA
TryGetValue_False_FrozenDictionary main 1000 1 964.540 ns 5.0478 ns 4.7217 ns 962.929 ns 959.170 ns 973.696 ns 1.00 0.01 - NA
TryGetValue_False_FrozenDictionary pr 1000 1 962.343 ns 8.6689 ns 7.2389 ns 960.085 ns 955.258 ns 979.858 ns 1.00 0.01 - NA
TryGetValue_True_FrozenDictionaryLookup main 1000 1 14,031.125 ns 45.7480 ns 40.5544 ns 14,022.326 ns 13,967.770 ns 14,109.108 ns 1.00 0.00 - NA
TryGetValue_True_FrozenDictionaryLookup pr 1000 1 4,775.847 ns 14.4875 ns 12.0977 ns 4,782.256 ns 4,756.205 ns 4,790.342 ns 0.34 0.00 - NA
TryGetValue_False_FrozenDictionaryLookup main 1000 1 10,419.083 ns 118.5014 ns 105.0484 ns 10,369.367 ns 10,325.502 ns 10,686.005 ns 1.00 0.01 - NA
TryGetValue_False_FrozenDictionaryLookup pr 1000 1 1,751.941 ns 4.3219 ns 3.6090 ns 1,752.104 ns 1,747.471 ns 1,760.421 ns 0.17 0.00 - NA
TryGetValue_True_FrozenDictionary main 1000 5 7,563.892 ns 55.5537 ns 49.2469 ns 7,545.850 ns 7,512.330 ns 7,672.709 ns 1.00 0.01 - NA
TryGetValue_True_FrozenDictionary pr 1000 5 7,818.676 ns 19.7715 ns 15.4363 ns 7,819.761 ns 7,793.648 ns 7,842.421 ns 1.03 0.01 - NA
TryGetValue_False_FrozenDictionary main 1000 5 915.250 ns 1.7844 ns 1.5818 ns 915.346 ns 912.014 ns 917.874 ns 1.00 0.00 - NA
TryGetValue_False_FrozenDictionary pr 1000 5 910.566 ns 12.8763 ns 12.0445 ns 914.497 ns 891.573 ns 929.127 ns 0.99 0.01 - NA
TryGetValue_True_FrozenDictionaryLookup main 1000 5 19,447.402 ns 170.0298 ns 150.7270 ns 19,370.762 ns 19,286.242 ns 19,729.391 ns 1.00 0.01 - NA
TryGetValue_True_FrozenDictionaryLookup pr 1000 5 11,133.069 ns 92.3892 ns 81.9006 ns 11,139.811 ns 10,872.462 ns 11,221.569 ns 0.57 0.01 - NA
TryGetValue_False_FrozenDictionaryLookup main 1000 5 10,437.497 ns 23.2264 ns 21.7260 ns 10,431.583 ns 10,410.619 ns 10,475.481 ns 1.00 0.00 - NA
TryGetValue_False_FrozenDictionaryLookup pr 1000 5 1,763.392 ns 10.4994 ns 8.7674 ns 1,766.299 ns 1,752.242 ns 1,776.463 ns 0.17 0.00 - NA
TryGetValue_True_FrozenDictionary main 10000 1 44,812.807 ns 236.4552 ns 221.1803 ns 44,755.250 ns 44,492.446 ns 45,244.679 ns 1.00 0.01 - NA
TryGetValue_True_FrozenDictionary pr 10000 1 46,652.041 ns 316.4328 ns 264.2357 ns 46,742.992 ns 45,857.045 ns 46,815.311 ns 1.04 0.01 - NA
TryGetValue_False_FrozenDictionary main 10000 1 24,743.250 ns 82.2079 ns 72.8752 ns 24,733.853 ns 24,628.742 ns 24,873.955 ns 1.00 0.00 - NA
TryGetValue_False_FrozenDictionary pr 10000 1 24,348.403 ns 128.3899 ns 120.0960 ns 24,298.454 ns 24,211.070 ns 24,574.111 ns 0.98 0.01 - NA
TryGetValue_True_FrozenDictionaryLookup main 10000 1 175,395.295 ns 2,061.6356 ns 1,928.4552 ns 175,724.157 ns 172,247.612 ns 179,175.772 ns 1.00 0.02 1 B 1.00
TryGetValue_True_FrozenDictionaryLookup pr 10000 1 73,207.400 ns 916.1985 ns 857.0126 ns 73,653.932 ns 71,877.670 ns 74,152.553 ns 0.42 0.01 - 0.00
TryGetValue_False_FrozenDictionaryLookup main 10000 1 187,889.132 ns 527.7729 ns 467.8569 ns 187,847.210 ns 187,278.943 ns 188,844.122 ns 1.00 0.00 1 B 1.00
TryGetValue_False_FrozenDictionaryLookup pr 10000 1 38,704.869 ns 477.7125 ns 446.8526 ns 38,588.712 ns 38,127.456 ns 39,391.137 ns 0.21 0.00 - 0.00
TryGetValue_True_FrozenDictionary main 10000 5 123,066.227 ns 746.0009 ns 697.8097 ns 122,825.843 ns 122,026.190 ns 124,332.341 ns 1.00 0.01 - NA
TryGetValue_True_FrozenDictionary pr 10000 5 123,737.585 ns 1,127.2591 ns 1,054.4389 ns 124,112.354 ns 122,209.229 ns 125,384.033 ns 1.01 0.01 1 B NA
TryGetValue_False_FrozenDictionary main 10000 5 21,487.233 ns 54.2720 ns 45.3195 ns 21,497.945 ns 21,402.457 ns 21,573.108 ns 1.00 0.00 - NA
TryGetValue_False_FrozenDictionary pr 10000 5 21,377.274 ns 56.0457 ns 43.7568 ns 21,374.646 ns 21,284.362 ns 21,442.488 ns 0.99 0.00 - NA
TryGetValue_True_FrozenDictionaryLookup main 10000 5 237,950.423 ns 2,776.5169 ns 2,597.1556 ns 236,306.250 ns 234,870.865 ns 241,643.365 ns 1.00 0.01 1 B 1.00
TryGetValue_True_FrozenDictionaryLookup pr 10000 5 149,147.012 ns 340.2931 ns 318.3104 ns 149,128.988 ns 148,492.798 ns 149,720.476 ns 0.63 0.01 - 0.00
TryGetValue_False_FrozenDictionaryLookup main 10000 5 135,478.543 ns 1,252.3472 ns 1,171.4464 ns 135,349.306 ns 134,010.897 ns 138,020.673 ns 1.00 0.01 - NA
TryGetValue_False_FrozenDictionaryLookup pr 10000 5 34,230.881 ns 322.2512 ns 301.4340 ns 34,058.756 ns 33,992.887 ns 34,774.708 ns 0.25 0.00 - NA
SingleCharFrozenDictionary
Method Toolchain Count Mean Error StdDev Median Min Max Ratio RatioSD Allocated Alloc Ratio
TryGetValue_True_FrozenDictionary main 10 25.03 ns 0.243 ns 0.228 ns 25.10 ns 24.57 ns 25.29 ns 1.00 0.01 - NA
TryGetValue_True_FrozenDictionary pr 10 24.90 ns 0.222 ns 0.186 ns 24.86 ns 24.67 ns 25.20 ns 0.99 0.01 - NA
TryGetValue_False_FrozenDictionary main 10 19.65 ns 0.162 ns 0.143 ns 19.63 ns 19.41 ns 19.94 ns 1.00 0.01 - NA
TryGetValue_False_FrozenDictionary pr 10 19.77 ns 0.305 ns 0.285 ns 19.70 ns 19.46 ns 20.37 ns 1.01 0.02 - NA
TryGetValue_True_FrozenDictionaryLookup main 10 140.36 ns 1.577 ns 1.475 ns 140.66 ns 137.81 ns 143.01 ns 1.00 0.01 - NA
TryGetValue_True_FrozenDictionaryLookup pr 10 62.38 ns 0.469 ns 0.416 ns 62.52 ns 61.58 ns 62.96 ns 0.44 0.01 - NA
TryGetValue_False_FrozenDictionaryLookup main 10 109.58 ns 0.625 ns 0.488 ns 109.66 ns 108.76 ns 110.50 ns 1.00 0.01 - NA
TryGetValue_False_FrozenDictionaryLookup pr 10 44.69 ns 0.432 ns 0.404 ns 44.44 ns 44.09 ns 45.20 ns 0.41 0.00 - NA
TryGetValue_True_FrozenDictionary main 100 233.23 ns 1.298 ns 1.084 ns 232.70 ns 232.12 ns 235.95 ns 1.00 0.01 - NA
TryGetValue_True_FrozenDictionary pr 100 235.70 ns 3.162 ns 2.803 ns 234.32 ns 232.76 ns 241.40 ns 1.01 0.01 - NA
TryGetValue_False_FrozenDictionary main 100 185.09 ns 1.358 ns 1.204 ns 184.73 ns 183.93 ns 188.03 ns 1.00 0.01 - NA
TryGetValue_False_FrozenDictionary pr 100 185.88 ns 1.629 ns 1.444 ns 185.39 ns 184.27 ns 189.42 ns 1.00 0.01 - NA
TryGetValue_True_FrozenDictionaryLookup main 100 1,393.50 ns 12.781 ns 11.956 ns 1,386.02 ns 1,382.84 ns 1,414.38 ns 1.00 0.01 - NA
TryGetValue_True_FrozenDictionaryLookup pr 100 629.05 ns 3.471 ns 3.246 ns 629.29 ns 623.57 ns 633.37 ns 0.45 0.00 - NA
TryGetValue_False_FrozenDictionaryLookup main 100 1,088.17 ns 11.327 ns 10.041 ns 1,087.82 ns 1,076.58 ns 1,107.27 ns 1.00 0.01 - NA
TryGetValue_False_FrozenDictionaryLookup pr 100 440.47 ns 2.459 ns 2.300 ns 440.07 ns 436.87 ns 445.37 ns 0.40 0.00 - NA
TryGetValue_True_FrozenDictionary main 1000 2,336.32 ns 22.456 ns 21.006 ns 2,326.63 ns 2,312.30 ns 2,374.77 ns 1.00 0.01 - NA
TryGetValue_True_FrozenDictionary pr 1000 2,353.42 ns 22.241 ns 20.805 ns 2,354.64 ns 2,328.50 ns 2,391.99 ns 1.01 0.01 - NA
TryGetValue_False_FrozenDictionary main 1000 1,840.32 ns 22.863 ns 21.386 ns 1,831.90 ns 1,816.67 ns 1,886.92 ns 1.00 0.02 - NA
TryGetValue_False_FrozenDictionary pr 1000 1,846.40 ns 11.044 ns 9.222 ns 1,847.02 ns 1,819.15 ns 1,858.81 ns 1.00 0.01 - NA
TryGetValue_True_FrozenDictionaryLookup main 1000 13,916.96 ns 120.841 ns 113.035 ns 13,860.22 ns 13,811.26 ns 14,182.77 ns 1.00 0.01 - NA
TryGetValue_True_FrozenDictionaryLookup pr 1000 6,188.74 ns 31.218 ns 29.202 ns 6,191.14 ns 6,092.64 ns 6,212.75 ns 0.44 0.00 - NA
TryGetValue_False_FrozenDictionaryLookup main 1000 10,887.83 ns 107.889 ns 95.641 ns 10,908.08 ns 10,760.05 ns 11,016.79 ns 1.00 0.01 - NA
TryGetValue_False_FrozenDictionaryLookup pr 1000 4,404.70 ns 36.506 ns 30.484 ns 4,415.44 ns 4,328.23 ns 4,426.62 ns 0.40 0.00 - NA
TryGetValue_True_FrozenDictionary main 10000 23,729.13 ns 237.473 ns 222.132 ns 23,613.20 ns 23,526.62 ns 24,179.01 ns 1.00 0.01 - NA
TryGetValue_True_FrozenDictionary pr 10000 23,474.17 ns 213.858 ns 200.043 ns 23,347.04 ns 23,302.09 ns 23,809.62 ns 0.99 0.01 - NA
TryGetValue_False_FrozenDictionary main 10000 18,378.85 ns 178.328 ns 158.083 ns 18,322.70 ns 18,160.98 ns 18,636.55 ns 1.00 0.01 - NA
TryGetValue_False_FrozenDictionary pr 10000 18,669.90 ns 102.371 ns 85.484 ns 18,700.51 ns 18,483.41 ns 18,790.11 ns 1.02 0.01 - NA
TryGetValue_True_FrozenDictionaryLookup main 10000 139,030.12 ns 1,236.323 ns 1,095.968 ns 138,585.77 ns 137,877.46 ns 141,096.93 ns 1.00 0.01 - NA
TryGetValue_True_FrozenDictionaryLookup pr 10000 61,809.42 ns 561.201 ns 497.490 ns 61,818.97 ns 61,136.20 ns 62,674.75 ns 0.44 0.00 - NA
TryGetValue_False_FrozenDictionaryLookup main 10000 108,375.03 ns 776.887 ns 726.701 ns 107,949.27 ns 107,731.72 ns 109,919.27 ns 1.00 0.01 - NA
TryGetValue_False_FrozenDictionaryLookup pr 10000 44,318.31 ns 405.006 ns 338.198 ns 44,431.91 ns 43,727.75 ns 44,770.61 ns 0.41 0.00 - NA
SubstringFrozenDictionary
Method Toolchain Count Mean Error StdDev Median Min Max Ratio RatioSD Allocated Alloc Ratio
TryGetValue_True_FrozenDictionary main 10 45.27 ns 0.500 ns 0.468 ns 45.44 ns 44.60 ns 45.85 ns 1.00 0.01 - NA
TryGetValue_True_FrozenDictionary pr 10 45.02 ns 0.415 ns 0.388 ns 45.16 ns 44.07 ns 45.47 ns 0.99 0.01 - NA
TryGetValue_False_FrozenDictionary main 10 40.94 ns 0.396 ns 0.351 ns 41.08 ns 40.23 ns 41.23 ns 1.00 0.01 - NA
TryGetValue_False_FrozenDictionary pr 10 40.96 ns 0.407 ns 0.339 ns 40.88 ns 40.26 ns 41.73 ns 1.00 0.01 - NA
TryGetValue_True_FrozenDictionaryLookup main 10 162.46 ns 1.747 ns 1.634 ns 162.78 ns 159.01 ns 164.37 ns 1.00 0.01 - NA
TryGetValue_True_FrozenDictionaryLookup pr 10 86.32 ns 0.231 ns 0.193 ns 86.25 ns 86.12 ns 86.83 ns 0.53 0.01 - NA
TryGetValue_False_FrozenDictionaryLookup main 10 134.82 ns 0.525 ns 0.465 ns 134.65 ns 134.17 ns 135.47 ns 1.00 0.00 - NA
TryGetValue_False_FrozenDictionaryLookup pr 10 66.03 ns 0.621 ns 0.581 ns 66.04 ns 65.37 ns 67.28 ns 0.49 0.00 - NA
TryGetValue_True_FrozenDictionary main 100 443.41 ns 2.826 ns 2.360 ns 443.62 ns 438.81 ns 446.23 ns 1.00 0.01 - NA
TryGetValue_True_FrozenDictionary pr 100 449.38 ns 3.174 ns 2.814 ns 450.13 ns 443.66 ns 453.04 ns 1.01 0.01 - NA
TryGetValue_False_FrozenDictionary main 100 401.50 ns 3.153 ns 2.949 ns 400.04 ns 398.29 ns 406.15 ns 1.00 0.01 - NA
TryGetValue_False_FrozenDictionary pr 100 405.78 ns 2.313 ns 2.050 ns 405.61 ns 400.66 ns 408.83 ns 1.01 0.01 - NA
TryGetValue_True_FrozenDictionaryLookup main 100 1,722.86 ns 11.579 ns 9.669 ns 1,721.56 ns 1,711.42 ns 1,748.19 ns 1.00 0.01 - NA
TryGetValue_True_FrozenDictionaryLookup pr 100 902.06 ns 8.528 ns 7.560 ns 905.34 ns 885.24 ns 910.35 ns 0.52 0.01 - NA
TryGetValue_False_FrozenDictionaryLookup main 100 1,510.85 ns 14.059 ns 13.151 ns 1,504.99 ns 1,496.83 ns 1,543.33 ns 1.00 0.01 - NA
TryGetValue_False_FrozenDictionaryLookup pr 100 675.14 ns 11.842 ns 11.077 ns 675.65 ns 662.45 ns 699.63 ns 0.45 0.01 - NA
TryGetValue_True_FrozenDictionary main 1000 4,668.98 ns 49.785 ns 41.573 ns 4,677.99 ns 4,577.36 ns 4,724.13 ns 1.00 0.01 - NA
TryGetValue_True_FrozenDictionary pr 1000 4,617.27 ns 18.507 ns 16.406 ns 4,614.33 ns 4,592.06 ns 4,651.68 ns 0.99 0.01 - NA
TryGetValue_False_FrozenDictionary main 1000 4,211.73 ns 49.914 ns 46.690 ns 4,202.37 ns 4,153.66 ns 4,298.69 ns 1.00 0.02 - NA
TryGetValue_False_FrozenDictionary pr 1000 4,223.29 ns 31.447 ns 24.552 ns 4,228.95 ns 4,164.89 ns 4,250.04 ns 1.00 0.01 - NA
TryGetValue_True_FrozenDictionaryLookup main 1000 17,170.39 ns 182.861 ns 171.049 ns 17,211.10 ns 16,890.11 ns 17,438.16 ns 1.00 0.01 - NA
TryGetValue_True_FrozenDictionaryLookup pr 1000 9,088.65 ns 86.163 ns 80.597 ns 9,080.74 ns 8,955.43 ns 9,243.28 ns 0.53 0.01 - NA
TryGetValue_False_FrozenDictionaryLookup main 1000 13,326.46 ns 46.269 ns 38.636 ns 13,327.35 ns 13,265.45 ns 13,407.01 ns 1.00 0.00 - NA
TryGetValue_False_FrozenDictionaryLookup pr 1000 6,814.56 ns 22.840 ns 20.247 ns 6,818.63 ns 6,784.58 ns 6,860.84 ns 0.51 0.00 - NA
TryGetValue_True_FrozenDictionary main 10000 65,610.00 ns 432.958 ns 383.806 ns 65,489.76 ns 65,173.58 ns 66,425.34 ns 1.00 0.01 - NA
TryGetValue_True_FrozenDictionary pr 10000 67,624.61 ns 887.689 ns 830.344 ns 67,540.84 ns 66,634.56 ns 68,936.38 ns 1.03 0.01 - NA
TryGetValue_False_FrozenDictionary main 10000 52,710.36 ns 497.305 ns 465.179 ns 52,784.55 ns 51,919.11 ns 53,485.47 ns 1.00 0.01 - NA
TryGetValue_False_FrozenDictionary pr 10000 51,204.82 ns 454.698 ns 403.078 ns 51,180.96 ns 50,629.71 ns 52,061.91 ns 0.97 0.01 - NA
TryGetValue_True_FrozenDictionaryLookup main 10000 226,872.14 ns 1,032.339 ns 965.651 ns 226,859.96 ns 224,795.47 ns 228,671.11 ns 1.00 0.01 - NA
TryGetValue_True_FrozenDictionaryLookup pr 10000 116,339.56 ns 301.862 ns 252.068 ns 116,278.31 ns 116,054.50 ns 116,958.27 ns 0.51 0.00 - NA
TryGetValue_False_FrozenDictionaryLookup main 10000 143,789.02 ns 1,235.081 ns 1,155.296 ns 143,830.91 ns 142,057.74 ns 146,136.64 ns 1.00 0.01 1 B 1.00
TryGetValue_False_FrozenDictionaryLookup pr 10000 76,627.32 ns 354.240 ns 295.806 ns 76,555.06 ns 76,329.85 ns 77,399.18 ns 0.53 0.00 - 0.00

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
Labels
area-System.Collections community-contribution Indicates that the PR has been added by a community member
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant