Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Faster Dictionary clone#41944

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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
eiriktsarpalis merged 1 commit intodotnet:masterfrombenaadams:Dcitionary-clone
Oct 13, 2020

Conversation

@benaadams
Copy link
Member

@benaadamsbenaadams commentedSep 7, 2020
edited
Loading

Resolves#41939

Fast.Clone() extension method can be made on top of this change as

publicstaticclassDictionaryExtensions{publicstaticDictionary<TKey,TValue>Clone<TKey,TValue>(thisDictionary<TKey,TValue>source)whereTKey:notnull{returnnewDictionary<TKey,TValue>(source,source.Comparer);}}

String keys are the biggest improvement since they cost most to rehash

MethodNetFxInitialCountMeanAllocatedRatioChange
Clone_Int_FullNetFx300062.09 us66.1 KB2.45x 0.4
Clone_Int_FullBase300025.32 us65.9 KB1.00-
Clone_Int_FullPR300010.56 us65.9 KB0.42x 2.4
Clone_Int_HalfRemovedNetFx300032.43 us31.4 KB2.73x 0.4
Clone_Int_HalfRemovedBase300011.88 us31.3 KB1.00-
Clone_Int_HalfRemovedPR30006.86 us31.3 KB0.58x 1.7
Clone_String_FullNetFx3000111.84 us92.4 KB1.86x 0.5
Clone_String_FullBase300060.01 us92.3 KB1.00-
Clone_String_FullPR300026.84 us92.3 KB0.45x 2.2
Clone_String_HalfRemovedNetFx300058.25 us43.9 KB1.39x 0.7
Clone_String_HalfRemovedBase300042.04 us43.8 KB1.00-
Clone_String_HalfRemovedPR300014.73 us43.8 KB0.35x 2.9

https://github.com/dotnet/performance/compare/master...benaadams:DictionaryClone?expand=1

@ghost
Copy link

Tagging subscribers to this area:@eiriktsarpalis,@jeffhandley
See info in area-owners.md if you want to be subscribed.

@mjsabby
Copy link
Contributor

@danmosemsft it would be nice if we also made this improvement in DictionarySlim. For specific cache-like use cases we roll our own dictionary and it would be nice to not have to do that.

@benaadams
Copy link
MemberAuthor

runtime (Libraries Test Run release coreclr Windows_NT x86 Release) issue#41494

@benaadams
Copy link
MemberAuthor

Helix gateway timeout on "runtime (Libraries Test Run release coreclr Windows_NT x86 Release)"

@benaadams
Copy link
MemberAuthor

Broken compress variant, but I'm quite impressed there are tests for these scenarios :)

@benaadamsbenaadamsforce-pushed theDcitionary-clone branch 2 times, most recently from0eea740 to36abff2CompareSeptember 8, 2020 05:15
@benaadamsbenaadamsforce-pushed theDcitionary-clone branch 2 times, most recently from387ec43 todc6f15eCompareSeptember 8, 2020 05:48
@jkotas
Copy link
Member

It would be nice to share performance numbers for the different cases.

@jkotasjkotas added the tenet-performancePerformance related issue labelSep 8, 2020
@danmoseley
Copy link
Member

@danmosemsft it would be nice if we also made this improvement in DictionarySlim. For specific cache-like use cases we roll our own dictionary and it would be nice to not have to do that.

@mjsabby the copy constructor specifically? It would be easy to add if someone would offer a PR.

I am unsure what to do with DictionarySlim. It's encouraging that you're using it, or plan to. That may suggest we should remove the "Experimental" or even put it into the core collections. But@GrabYourPitchforks had concerns about it's "unsafeness" (I think related to mutating the entry in place). Also the name may suggest to people it's optimized for small number of items, which it's not (I happened to just run across@benaadamstype with the same name)

@GrabYourPitchforks
Copy link
Member

The code LGTM. But we don't have evidence that this is a hot path for anybody.

I'm somewhat paranoid that we're going to use this code as a new performance profile, then in the future we'll discover some obscure edge case which means we need to go down a somewhat slower path, then some automated system will kick in and say "Ah, but you regressed this method's performance by 17%!", and then we'll spend days chasing it down - all while continuing not to have evidence that this is actually a hot path for real-world apps. Basically, I'm afraid that it's signing us up for continuing maintenance work for a code path that we didn't want to invest significant resources into.

@benaadams
Copy link
MemberAuthor

all while continuing not to have evidence that this is actually a hot path for real-world apps.

Signals Rosyln would move away from ImmuntableDictionary for a fast Dictionary clone methoddotnet/roslyn#47503 (comment)

Yes, ImmutableDictionary is really bad. We just haven't had cases yet that fell into the patterns for which we have production-ready alternatives. The main scenario that we have the ability to improve today is collections with the following characteristics:

  • The dictionary is built up from empty, as opposed to built by transforming a previous collection through add/remove operations
  • The collection has more than a few elements, but less than ~10K elements

For this we could switch to an immutable wrapper aroundDictionary<TKey, TValue>, similar to how we useImmutableArray<T> instead ofImmutableList<T>.

/cc@sharwell

and

My gut tells me this should be a slam dunk win. The vast majority (probably nearly all) of cases where we use ImmutableDict, it's not to benefit from what ID is decent at, but just to ensure it will not be mutated post creation. For that purpose it's actually a pretty terrible choice and adds a tremendous amount of overhead for functionality that we're not needing in those scenarios.

/cc@CyrusNajmabadi

Also for lockless read Dictionary, with very infrequent updates (where reallocating the entire dictionary is more lightweight than taking a lock on every read, or usingConcurrentDictionary) e.g.

classLookupTable{privateDictionary<int,string>?_lookUp;privatereadonlyobject_lock=new();publicboolTryGetValue(intkey,[MaybeNullWhen(false)]outstringvalue){// No locks required for readvarlookUp=_lookUp;if(lookUpisnull){value=default;returnfalse;}returnlookUp.TryGetValue(key,outvalue);}publicvoidAdd(intkey,stringvalue){lock(_lock){// Clone + Addvardict=_lookUp?.Clone()??new(capacity:1);dict.Add(key,value);// Switch reference_lookUp=dict;}}publicvoidAddRange(IEnumerable<KeyValuePair<int,string>>collection){lock(_lock){// Clone + Addvardict=_lookUp?.Clone()??new(capacity:8);foreach(varentryincollection){dict.Add(entry.Key,entry.Value);}// Switch reference_lookUp=dict;}}}
GrabYourPitchforks, mjsabby, and gfoidl reacted with thumbs up emoji

@benaadams
Copy link
MemberAuthor

Its also why I wanted to add a clone extension method in#41939

publicstaticclassDictionaryExtensions{publicstaticDictionary<TKey,TValue>Clone<TKey,TValue>(thisDictionary<TKey,TValue>source)whereTKey:notnull{returnnewDictionary<TKey,TValue>(source,source.Comparer);}}

Because it's not clear that passing aDictionary as anIDictionary to theDictionary constructor is a fast way to copy it and on Full Framework it really isn'thttps://referencesource.microsoft.com/#mscorlib/system/collections/generic/dictionary.cs,106

@benaadams
Copy link
MemberAuthor

ReleasedTrimExcess andCopy might as well share the same entity copying code

@jkotas
Copy link
Member

Released TrimExcess and Copy might as well share the same entity copying code

Nice!

Copy link
Member

@jkotasjkotas left a comment

Choose a reason for hiding this comment

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

LGTM. Thanks!

@eiriktsarpalis
Copy link
Member

Is there any pending feedback on this PR? If not I'll just merge.

@eiriktsarpaliseiriktsarpalis added this to the6.0.0 milestoneSep 30, 2020
@eiriktsarpaliseiriktsarpalis self-assigned thisSep 30, 2020
@benaadams
Copy link
MemberAuthor

Don't think so; CI wasn't behaving (one job failed in unrelated area), when it was was going to rebase for a clean set of greens, but looking at the open PRs doesn't look like CI is there yet...

@benaadams
Copy link
MemberAuthor

Squashed and Rebased

@eiriktsarpalis
Copy link
Member

Failing tests seem like unrelated timeouts.

@eiriktsarpaliseiriktsarpalis merged commita4b566a intodotnet:masterOct 13, 2020
@eiriktsarpalis
Copy link
Member

Thanks@benaadams!

benaadams reacted with thumbs up emoji

@benaadamsbenaadams deleted the Dcitionary-clone branchOctober 13, 2020 16:37
@benaadams
Copy link
MemberAuthor

Aside it should also improveSparseTensor<T>.Clone() and.ToSparseTensor()

publicoverrideTensor<T>Clone()
{
varvalueCopy=newDictionary<int,T>(values);
returnnewSparseTensor<T>(valueCopy,dimensions,IsReversedStride);
}

publicoverrideSparseTensor<T>ToSparseTensor()
{
varvalueCopy=newDictionary<int,T>(values);
returnnewSparseTensor<T>(valueCopy,dimensions,IsReversedStride);
}

@DrewScoggins
Copy link
Member

Just wanted to update that we are seeing these wins in the performance lab for both strings and ints.

Run Information

Architecturex64
OSWindows 10.0.18362
Changesdiff

Regressions in System.Collections.CtorFromCollection

BenchmarkBaselineTestTest/BaseModalityBaseline OutlierBaseline ETLComapre ETL
Dictionary15.46 μs4.37 μs0.28True

graph
Historical Data in Reporting System

Repro

git clone https://github.com/dotnet/performance.gitpy .\performance\scripts\benchmarks_ci.py -f netcoreapp5.0 --filter 'System.Collections.CtorFromCollection<String>*'

Histogram

System.Collections.CtorFromCollection.Dictionary(Size: 512)

[ 3227.117 ;  5690.502) | @@@@@@@@@@@@@@@@@@@@[ 5690.502 ;  7941.971) | [ 7941.971 ; 10193.440) | [10193.440 ; 12444.908) | [12444.908 ; 14336.416) | [14336.416 ; 16587.885) | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Docs

Profiling workflow for dotnet/runtime repository
Benchmarking workflow for dotnet/runtime repository

Run Information

Architecturex64
OSWindows 10.0.18362
Changesdiff

Regressions in System.Collections.CtorFromCollection

BenchmarkBaselineTestTest/BaseModalityBaseline OutlierBaseline ETLComapre ETL
Dictionary6.25 μs1.94 μs0.31True

graph
Historical Data in Reporting System

Repro

git clone https://github.com/dotnet/performance.gitpy .\performance\scripts\benchmarks_ci.py -f netcoreapp5.0 --filter 'System.Collections.CtorFromCollection<Int32>*'

Histogram

System.Collections.CtorFromCollection.Dictionary(Size: 512)

[1496.277 ; 2414.652) | @@@@@@@@@@@@@@@@@@@@[2414.652 ; 3307.499) | [3307.499 ; 4200.346) | [4200.346 ; 5093.193) | [5093.193 ; 5775.253) | [5775.253 ; 6668.100) | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@[6668.100 ; 7562.448) | @@@@@@@@@@@@@

Docs

Profiling workflow for dotnet/runtime repository
Benchmarking workflow for dotnet/runtime repository

danmoseley and benaadams reacted with heart emoji

@danmoseley
Copy link
Member

Love it - thank you@DrewScoggins for following up with that positive data.

@ghostghost locked asresolvedand limited conversation to collaboratorsDec 7, 2020
Sign up for freeto subscribe to this conversation on GitHub. Already have an account?Sign in.

Reviewers

@stephentoubstephentoubstephentoub left review comments

@jkotasjkotasjkotas approved these changes

@GrabYourPitchforksGrabYourPitchforksGrabYourPitchforks approved these changes

@eiriktsarpaliseiriktsarpaliseiriktsarpalis approved these changes

@jeffhandleyjeffhandleyAwaiting requested review from jeffhandley

Assignees

@eiriktsarpaliseiriktsarpalis

Labels

Projects

None yet

Milestone

6.0.0

Development

Successfully merging this pull request may close these issues.

Fast Dictionary<K,V>.Clone() api

11 participants

@benaadams@mjsabby@jkotas@danmoseley@GrabYourPitchforks@CyrusNajmabadi@333fred@eiriktsarpalis@DrewScoggins@stephentoub@Dotnet-GitSync-Bot

[8]ページ先頭

©2009-2025 Movatter.jp