-
Notifications
You must be signed in to change notification settings - Fork 4.7k
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
Backport improvements from DictionarySlim to Dictionary #27877
Comments
Store
|
@benaadams yes, clarified above |
@MarcoRossignoli this may be interesting to you. It would require perf measurements using the new perf repo. |
@danmosemsft interesting, I'll need some time to understand amendments and new repo organization, but if there's no rush I'm happy to try!
Correct? |
@MarcoRossignoli actually im not sure there is anything to port from corefxlab. The existing dictionary perf tests have moved from corefx repo to dotnet/performance repo. Those are the ones to run. |
Ah ok perfect!So we need only amend and do comparison? |
@MarcoRossignoli yes there should be enough perf test coverage already, and Adam has made it super easy to run the tests in the perf repo Would you like me to assign this to you? |
Yes I'm on it right now, https://github.com/MarcoRossignoli/marcorossignoli.github.io/compare/dicbackporting dotnet/BenchmarkDotNet#1031 |
😄 yes don't hesitate to ping code reviewers. |
Oh yes |
Above fails, I would avoid
/cc @jkotas @ViktorHofer EDIT: Could we use new startups hook to find and wait IsAttacched on-the-fly? |
Why can't you set a breakpoint in the unit test in corefx and step into the CoreLib code you are interested in? Debugging with VS 2019 >= Preview 2 should work fine again. |
@ViktorHofer you mean compile coreclr in debug compile corefx with local coreclr in debug and breakpoint should work?(tried in past but with no luck) |
It shouldn't really matter if debug or release but in debug you will get more accurate debugging results. |
Debug coreclr (which means debug corelib and coreclr.dll) is dog slow of course. So I'd try release first 😼 |
I saw, for the moment I moved out some code for fast debugging. |
Thank's @ViktorHofer confirm that it works well. |
From the PR, which is now just for entropy part, you said:
I spent a little time to absorb this. Resummarizing the problem for myself -- Previously: _count was the “high water mark” in the backing array. Actual count is “_count - _freeCount”. If there are no free (ie _freeCount is 0), adds are inserted after "_count". When iterating, walk the array no further than “_count” to avoid wasted time. After: _count is the actual count of items in the backing array. Actual count is “_count”. If there are no free (ie _freeList == -1), adds are inserted after “_count”. When iterating, keep count of free entries encountered, and walk no further than “_count + free entries encountered. The problem being the added cost of doing this sum. For CopyTo (much the same as enumeration), the code was int count = _dictionary._count;
for (int i = 0; i < count; i++)
{
if (entries[i].hashCode >= 0) array[index++] = entries[i].key;
} After, it was int count = _dictionary._count;
for (int i = 0; count > 0 && i < entries.Length; i++)
{
// entries < -1 are in freelist
if (entries[i].next >= -1)
{
array[index++] = entries[i].key;
count--;
}
} You had to include the int count = 0;
for (int i = 0; count < _dictionary._count; i++)
{
// entries < -1 are in freelist
if (entries[i].next >= -1)
{
array[index++] = entries[i].key;
count++;
}
} Another approach could be to follow the free list chain first, to get the free count, then the loop is back to the simple comparison with no counter required. But that traversal cost could be arbitrarily large. I may think some more about this but it does seem you are right that this will always be less efficient for enumeation. That is worse than saving a field (and enumerator adds a field). Thanks for looking. |
dotnet/coreclr#23591 completes this. |
Keeping a list of changes made in DictionarySlim that could benefit Dictionary
& 0x7FFFFFFF;
to remove the sign bit on the hash code, use(uint)
cast and store a uint hashcode, retaining more entropy. To store 'empty' instead of hashcode of -1 use this scheme:_freeCount
field and use_freeList == -1
as a sentinel insteadEliminate_buckets == null
check on every access, use a dummy 1-element array instead, on which reads fail and adds cause a resize into a real arraySome of these apply to
HashSet<T>
also.@AnthonyLloyd @Zhentar
The text was updated successfully, but these errors were encountered: