-
Notifications
You must be signed in to change notification settings - Fork 5.3k
Don't box every single value type dictionary key. #46460
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
Conversation
da5acaa to
43ef1d7
Compare
|
This is great. How did you detect it?
Can we run some benchmarks to get a sense of how much heap allocations we save? It might help modifying and running this benchmark on a dictionary input that you expect to get performance wins on (with value type keys). Please share the benchmarkdotnet results when you have them: |
....Json/src/System/Text/Json/Serialization/Converters/Collection/DictionaryDefaultConverter.cs
Outdated
Show resolved
Hide resolved
....Json/src/System/Text/Json/Serialization/Converters/Collection/DictionaryDefaultConverter.cs
Outdated
Show resolved
Hide resolved
I am currently working on the high-performance dynamodb library which involves some low-level optimized JSON deserialization. I was able to detect several inefficiencies during some internal benchmark comparisons. Here is the benchmark result for BenchmarkDotNet=v0.12.1.1466-nightly, OS=Windows 10.0.19041.572 (2004/May2020Update/20H1)
Intel Core i7-7820X CPU 3.60GHz (Kaby Lake), 1 CPU, 6 logical and 3 physical cores
.NET SDK=6.0.100-alpha.1.20630.6
[Host] : .NET 6.0.0 (6.0.20.62903), X64 RyuJIT
Job-NDNXAQ : .NET 6.0.0 (42.42.42.42424), X64 RyuJIT
Job-EVXCXL : .NET 6.0.0 (42.42.42.42424), X64 RyuJIT
PowerPlanMode=00000000-0000-0000-0000-000000000000 Arguments=/p:DebugType=portable IterationTime=250.0000 ms
MaxIterationCount=20 MinIterationCount=15 WarmupCount=1
It is also worth noting another potential optimization that I am considering to create a PR for. On the benchmark, you can see that deserialization from |
During slow path deserialization fallback to storing dictionary key as object only when Read returns false.
43ef1d7 to
f2fb38f
Compare
steveharter
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks!
|
Looks like we have a couple approvals: Is this ready to merge? Might want to get fresh test results (current are from Jan 5) |
layomia
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the PR; this good to merge. The perf results shouldn't be stale given the changes.
It is also worth noting another potential optimization that I am considering to create a PR for. On the benchmark, you can see that deserialization from Stream is visibly slower than from bytes array even when entire JSON fits in a single buffer. I suspect it happens because we don't use UseFastPath for a single buffer case. But it is unrelated and probably not worth discussing in this thread.
We'd be happy to take a PR exploring this if we can detect a single buffer case.
Deserialization of dictionaries from
Streamalways causes keys boxing for value types becauseReadStackFrame.DictionaryKeyis an object. Saving key in the state (boxing) is only needed when value can't be fully read (Readcall returnsfalse).In the best-case scenario when an entire dictionary fits in a buffer - it should completely remove unnecessary heap allocations for keys.