-
Notifications
You must be signed in to change notification settings - Fork 4.8k
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
String Dictionary with StringComparer.Ordinal slower than default EqualityComparer #29714
Comments
There are a number of optimizations to the dictionary and the handling of the default comparers by the jit -- for example, dotnet/coreclr#14125, dotnet/coreclr#15419). So not too surprising that this is now the fastest method. I have not looked at the ordinal comparer perf; perhaps it would be worth investigating. |
Why would you expect it to be faster? |
I think mostly because of Microsoft documentation about strings and their article about best practices docs best practices But the |
|
@stephentoub What if The This would add one reference equality check to the constructor and one field access to Or would this have issues with inlining or devirtualization that I'm missing? The code diff would roughly look like this: @@ -84,6 +84,10 @@ public Dictionary(int capacity, IEqualityComparer<TKey>? comparer)
// To start, move off default comparer for string which is randomised
_comparer = (IEqualityComparer<TKey>)NonRandomizedStringEqualityComparer.Default;
}
+ else if (typeof(TKey) == typeof(string) && _comparer == StringComparer.Ordinal)
+ {
+ _comparer = (IEqualityComparer<TKey>)NonRandomizedStringEqualityComparer.Ordinal;
+ }
}
public Dictionary(IDictionary<TKey, TValue> dictionary) : this(dictionary, null) { }
@@ -149,7 +153,11 @@ public IEqualityComparer<TKey> Comparer
{
get
{
- return (_comparer == null || _comparer is NonRandomizedStringEqualityComparer) ? EqualityComparer<TKey>.Default : _comparer;
+ return _comparer switch {
+ null => EqualityComparer<TKey>.Default,
+ NonRandomizedStringEqualityComparer nr => (IEqualityComparer<TKey>)nr.BackingComparer,
+ _ => _comparer
+ };
}
}
@@ -662,11 +670,11 @@ private bool TryInsert(TKey key, TValue value, InsertionBehavior behavior)
_version++;
// Value types never rehash
- if (default(TKey)! == null && collisionCount > HashHelpers.HashCollisionThreshold && comparer is NonRandomizedStringEqualityComparer) // TODO-NULLABLE: default(T) == null warning (https://github.com/dotnet/roslyn/issues/34757)
+ if (default(TKey)! == null && collisionCount > HashHelpers.HashCollisionThreshold && comparer is NonRandomizedStringEqualityComparer nr) // TODO-NULLABLE: default(T) == null warning (https://github.com/dotnet/roslyn/issues/34757)
{
// If we hit the collision threshold we'll need to switch to the comparer which is using randomized string hashing
// i.e. EqualityComparer<string>.Default.
- _comparer = null;
+ _comparer = (IEqualityComparer<TKey>)(nr.BackingComparer == EqualityComparer<string>.Default ? null : nr.BackingComparer);
Resize(entries.Length, true);
} |
If it can be done without breaking anything, without measurable overheads, and with measurable improvements, sure. |
Due to lack of recent activity, this issue has been marked as a candidate for backlog cleanup. It will be closed if no further activity occurs within 14 more days. Any new comment (by anyone, not necessarily the author) will undo this process. This process is part of the experimental issue cleanup initiative we are currently trialing in a limited number of areas. Please share any feedback you might have in the linked issue. |
This issue will now be closed since it had been marked |
Hello.
As I understand it, a
Dictionary
with it's key asstring
and usingStringComparer.Ordinal
should be faster than with a defaultEqualityComparer
.I did some tests with
BenchmarkDotNet
and it seemed true for.NET Framework
but not for.NET CORE
.Maybe I'm doing something wrong?
Bellow are the results, the code for the tests and the
csproj
file.The text was updated successfully, but these errors were encountered: