-
Notifications
You must be signed in to change notification settings - Fork 4.9k
Populate non-immutable collections directly on deserialize #41482
Conversation
@layomia can you please sync to latest code and include the benchmark results (before\after). Thanks |
(rather than storing in temporary collections and using converters to create and populate instances)
DeserializationStringToStringDictionaryWrapper (type that implements Dictionary<string, string>)29% faster, 34.18% decrease in allocations. Before
After
ImmutableSortedDictionary11% faster, no change in allocations Before
After
Hashtable7% faster, 10.54% decrease in allocations. Before
After
|
Method | Mean | Error | StdDev | Median | Min | Max | Gen 0 | Gen 1 | Gen 2 | Allocated |
---|---|---|---|---|---|---|---|---|---|---|
DeserializeFromString | 13.75 us | 0.053 us | 0.050 us | 13.75 us | 13.68 us | 13.84 us | 2.9248 | - | - | 12.03 KB |
After
Method | Mean | Error | StdDev | Median | Min | Max | Gen 0 | Gen 1 | Gen 2 | Allocated |
---|---|---|---|---|---|---|---|---|---|---|
DeserializeFromString | 12.98 us | 0.056 us | 0.050 us | 12.98 us | 12.89 us | 13.06 us | 2.3976 | 0.0521 | - | 9.86 KB |
ICollection
4% faster, no change to allocations
Before
Method | Mean | Error | StdDev | Median | Min | Max | Gen 0 | Gen 1 | Gen 2 | Allocated |
---|---|---|---|---|---|---|---|---|---|---|
DeserializeFromString | 55.73 us | 0.247 us | 0.219 us | 55.72 us | 55.45 us | 56.28 us | 7.2218 | 0.2257 | - | 30.29 KB |
After
Method | Mean | Error | StdDev | Median | Min | Max | Gen 0 | Gen 1 | Gen 2 | Allocated |
---|---|---|---|---|---|---|---|---|---|---|
DeserializeFromString | 53.71 us | 0.158 us | 0.140 us | 53.72 us | 53.49 us | 53.93 us | 7.3387 | 0.2158 | - | 30.29 KB |
BinaryData
4% faster, no change to allocations.
Before
Method | Mean | Error | StdDev | Median | Min | Max | Gen 0 | Gen 1 | Gen 2 | Allocated |
---|---|---|---|---|---|---|---|---|---|---|
DeserializeFromString | 705.0 ns | 9.12 ns | 8.53 ns | 705.7 ns | 689.8 ns | 718.0 ns | 0.2550 | - | - | 1.05 KB |
After
Method | Mean | Error | StdDev | Median | Min | Max | Gen 0 | Gen 1 | Gen 2 | Allocated |
---|---|---|---|---|---|---|---|---|---|---|
DeserializeFromString | 679.3 ns | 6.70 ns | 5.94 ns | 680.4 ns | 669.2 ns | 687.5 ns | 0.2542 | - | - | 1.05 KB |
ArrayList
3% faster, 5.17% decrease in allocations
Before
Method | Mean | Error | StdDev | Median | Min | Max | Gen 0 | Gen 1 | Gen 2 | Allocated |
---|---|---|---|---|---|---|---|---|---|---|
DeserializeFromString | 57.28 us | 0.300 us | 0.281 us | 57.29 us | 56.65 us | 57.77 us | 7.7750 | - | - | 31.93 KB |
After
Method | Mean | Error | StdDev | Median | Min | Max | Gen 0 | Gen 1 | Gen 2 | Allocated |
---|---|---|---|---|---|---|---|---|---|---|
DeserializeFromString | 55.40 us | 0.220 us | 0.206 us | 55.40 us | 55.11 us | 55.75 us | 7.2321 | 0.2192 | - | 30.29 KB |
LoginViewModel
3% faster, no change to allocations
Before
Method | Mean | Error | StdDev | Median | Min | Max | Gen 0 | Gen 1 | Gen 2 | Allocated |
---|---|---|---|---|---|---|---|---|---|---|
DeserializeFromString | 555.9 ns | 5.94 ns | 5.56 ns | 555.4 ns | 548.7 ns | 565.5 ns | 0.0378 | - | - | 168 B |
After
Method | Mean | Error | StdDev | Median | Min | Max | Gen 0 | Gen 1 | Gen 2 | Allocated |
---|---|---|---|---|---|---|---|---|---|---|
DeserializeFromString | 540.3 ns | 4.97 ns | 4.65 ns | 538.9 ns | 534.8 ns | 550.7 ns | 0.0387 | - | - | 168 B |
Queue
3% faster, 5.02% decrease in allocations.
Before
Method | Mean | Error | StdDev | Median | Min | Max | Gen 0 | Gen 1 | Gen 2 | Allocated |
---|---|---|---|---|---|---|---|---|---|---|
DeserializeFromString | 57.44 us | 0.161 us | 0.134 us | 57.41 us | 57.25 us | 57.72 us | 7.7063 | - | - | 31.67 KB |
After
Method | Mean | Error | StdDev | Median | Min | Max | Gen 0 | Gen 1 | Gen 2 | Allocated |
---|---|---|---|---|---|---|---|---|---|---|
DeserializeFromString | 55.83 us | 0.182 us | 0.170 us | 55.80 us | 55.54 us | 56.17 us | 7.1445 | - | - | 30.08 KB |
WrapperForIList (custom type that implements IList)
3% faster, 3.75% decrease in allocations
Before
Method | Mean | Error | StdDev | Median | Min | Max | Gen 0 | Gen 1 | Gen 2 | Allocated |
---|---|---|---|---|---|---|---|---|---|---|
DeserializeFromString | 57.61 us | 0.187 us | 0.166 us | 57.63 us | 57.24 us | 57.90 us | 7.7679 | 0.2285 | - | 32.49 KB |
After
Method | Mean | Error | StdDev | Median | Min | Max | Gen 0 | Gen 1 | Gen 2 | Allocated |
---|---|---|---|---|---|---|---|---|---|---|
DeserializeFromString | 56.23 us | 0.602 us | 0.533 us | 56.04 us | 55.71 us | 57.65 us | 7.3155 | - | - | 30.31 KB |
ISet
No chance to execution time, 12.70% decrease in allocations.
Before
Method | Mean | Error | StdDev | Median | Min | Max | Gen 0 | Gen 1 | Gen 2 | Allocated |
---|---|---|---|---|---|---|---|---|---|---|
DeserializeFromString | 18.04 us | 0.068 us | 0.063 us | 18.02 us | 17.90 us | 18.15 us | 4.1724 | 0.2877 | - | 17.09 KB |
After
Method | Mean | Error | StdDev | Median | Min | Max | Gen 0 | Gen 1 | Gen 2 | Allocated |
---|---|---|---|---|---|---|---|---|---|---|
DeserializeFromString | 18.27 us | 0.109 us | 0.102 us | 18.24 us | 18.12 us | 18.50 us | 3.6478 | 0.2918 | - | 14.92 KB |
WrapperForIList (type that implements IList)
No change to execution time, 6.71% decrease in allocations
Before
Method | Mean | Error | StdDev | Median | Min | Max | Gen 0 | Gen 1 | Gen 2 | Allocated |
---|---|---|---|---|---|---|---|---|---|---|
DeserializeFromString | 57.61 us | 0.187 us | 0.166 us | 57.63 us | 57.24 us | 57.90 us | 7.7679 | 0.2285 | - | 32.49 KB |
After
Method | Mean | Error | StdDev | Median | Min | Max | Gen 0 | Gen 1 | Gen 2 | Allocated |
---|---|---|---|---|---|---|---|---|---|---|
DeserializeFromString | 56.23 us | 0.602 us | 0.533 us | 56.04 us | 55.71 us | 57.65 us | 7.3155 | - | - | 30.31 KB |
No significant perf changes to ICollection<string>
, Location, MyEventsListerViewModel, WrapperForStringList (type that implements List<string>
), ImmutableDictionary<string, string>, List<string>
, IImmutableList<string>
, ImmutableArray<string>
, IndexViewModel
SortedList
4% slower, 16.60% decrease in allocations
Before
Method | Mean | Error | StdDev | Median | Min | Max | Gen 0 | Gen 1 | Gen 2 | Allocated |
---|---|---|---|---|---|---|---|---|---|---|
DeserializeFromString | 94.16 us | 0.525 us | 0.492 us | 94.28 us | 93.35 us | 95.23 us | 11.6541 | - | - | 48.07 KB |
After
Method | Mean | Error | StdDev | Median | Min | Max | Gen 0 | Gen 1 | Gen 2 | Allocated |
---|---|---|---|---|---|---|---|---|---|---|
DeserializeFromString | 98.22 us | 0.256 us | 0.240 us | 98.16 us | 97.81 us | 98.76 us | 9.5238 | 1.5873 | - | 40.09 KB |
Serialization
ArrayList
6% faster, no change in allocations
Before
Method | Mean | Error | StdDev | Median | Min | Max | Gen 0 | Gen 1 | Gen 2 | Allocated |
---|---|---|---|---|---|---|---|---|---|---|
SerializeToString | 17.30 us | 0.073 us | 0.064 us | 17.29 us | 17.22 us | 17.44 us | 1.4557 | - | - | 6 KB |
After
Method | Mean | Error | StdDev | Median | Min | Max | Gen 0 | Gen 1 | Gen 2 | Allocated |
---|---|---|---|---|---|---|---|---|---|---|
SerializeToString | 16.39 us | 0.060 us | 0.050 us | 16.37 us | 16.34 us | 16.51 us | 1.4417 | - | - | 6 KB |
SortedList
4% faster, no change to allocations.
Before
Method | Mean | Error | StdDev | Median | Min | Max | Gen 0 | Gen 1 | Gen 2 | Allocated |
---|---|---|---|---|---|---|---|---|---|---|
SerializeToString | 32.41 us | 0.166 us | 0.139 us | 32.39 us | 32.19 us | 32.68 us | 4.3911 | 0.1291 | - | 18.04 KB |
After
Method | Mean | Error | StdDev | Median | Min | Max | Gen 0 | Gen 1 | Gen 2 | Allocated |
---|---|---|---|---|---|---|---|---|---|---|
SerializeToString | 31.09 us | 0.089 us | 0.084 us | 31.07 us | 30.95 us | 31.26 us | 4.3679 | 0.1248 | - | 18.04 KB |
MyEventsListerViewModel
3% faster, no change to allocations
Before
Method | Mean | Error | StdDev | Median | Min | Max | Gen 0 | Gen 1 | Gen 2 | Allocated |
---|---|---|---|---|---|---|---|---|---|---|
SerializeToString | 568.5 us | 3.77 us | 3.34 us | 568.2 us | 562.6 us | 574.8 us | 92.3788 | 46.1894 | 46.1894 | 386.49 KB |
After
Method | Mean | Error | StdDev | Median | Min | Max | Gen 0 | Gen 1 | Gen 2 | Allocated |
---|---|---|---|---|---|---|---|---|---|---|
SerializeToString | 548.9 us | 1.01 us | 0.89 us | 549.0 us | 547.2 us | 550.2 us | 92.5110 | 46.2555 | 46.2555 | 386.49 KB |
ImmutableSortedDictionary
3% faster, no change to allocations
Before
Method | Mean | Error | StdDev | Median | Min | Max | Gen 0 | Gen 1 | Gen 2 | Allocated |
---|---|---|---|---|---|---|---|---|---|---|
SerializeToString | 42.04 us | 0.103 us | 0.096 us | 42.03 us | 41.88 us | 42.25 us | 4.3912 | - | - | 18.2 KB |
After
Method | Mean | Error | StdDev | Median | Min | Max | Gen 0 | Gen 1 | Gen 2 | Allocated |
---|---|---|---|---|---|---|---|---|---|---|
SerializeToString | 40.90 us | 0.137 us | 0.121 us | 40.87 us | 40.70 us | 41.19 us | 4.4017 | - | - | 18.2 KB |
ImmutableArray<string>
3% slower, no change to allocations
Before
Method | Mean | Error | StdDev | Median | Min | Max | Gen 0 | Gen 1 | Gen 2 | Allocated |
---|---|---|---|---|---|---|---|---|---|---|
SerializeToString | 12.54 us | 0.055 us | 0.052 us | 12.53 us | 12.48 us | 12.68 us | 1.4532 | - | - | 6.01 KB |
After
Method | Mean | Error | StdDev | Median | Min | Max | Gen 0 | Gen 1 | Gen 2 | Allocated |
---|---|---|---|---|---|---|---|---|---|---|
SerializeToString | 12.86 us | 0.055 us | 0.046 us | 12.86 us | 12.77 us | 12.94 us | 1.4346 | - | - | 6.01 KB |
LinkedList<string
4% slower
Before
Method | Mean | Error | StdDev | Median | Min | Max | Gen 0 | Gen 1 | Gen 2 | Allocated |
---|---|---|---|---|---|---|---|---|---|---|
SerializeToString | 13.26 us | 0.036 us | 0.034 us | 13.26 us | 13.21 us | 13.32 us | 1.4324 | - | - | 6 KB |
After
Method | Mean | Error | StdDev | Median | Min | Max | Gen 0 | Gen 1 | Gen 2 | Allocated |
---|---|---|---|---|---|---|---|---|---|---|
SerializeToString | 13.73 us | 0.040 us | 0.033 us | 13.74 us | 13.69 us | 13.82 us | 1.4251 | - | - | 6 KB |
No significant perf changes to serializing Queue, ICollection, ISet<string>
, WrapperForListString
, Stack<string>
, ImmutableDictionary<string, string>, StringICollectionWrapper (type that implements ICollection), StringToStringDictionaryWrapper
, HashSet<string>
, ICollection<string>
.
Execution time summary
Faster
Faster | base/diff | Base Median (ns) | Diff Median (ns) | Modality |
---|---|---|---|---|
System.Text.Json.Serialization.Tests.ReadJson.D | 1.29 | 31885.20 | 24773.73 | |
System.Text.Json.Serialization.Tests.ReadJson<ImmutableSortedDictionary<String, | 1.11 | 151214.23 | 136378.11 | |
System.Text.Json.Serialization.Tests.ReadJson.DeserializeFromString | 1.07 | 77308.23 | 72353.13 | |
System.Text.Json.Serialization.Tests.ReadJson<Stack>.DeserializeFromStri | 1.06 | 13751.54 | 12983.48 | |
System.Text.Json.Serialization.Tests.WriteJson.SerializeToString | 1.06 | 17287.58 | 16373.20 | |
System.Text.Json.Serialization.Tests.WriteJson.SerializeToString | 1.04 | 32386.89 | 31074.00 | |
System.Text.Json.Serialization.Tests.WriteJson.SerializeToString | 1.04 | 33390.37 | 32181.77 | |
System.Text.Json.Serialization.Tests.ReadJson.DeserializeFromString | 1.04 | 55717.64 | 53715.31 | |
System.Text.Json.Serialization.Tests.ReadJson.DeserializeFromString | 1.04 | 705.66 | 680.36 | |
System.Text.Json.Serialization.Tests.WriteJson.Serializ | 1.03 | 568226.21 | 549017.73 | |
System.Text.Json.Serialization.Tests.ReadJson.DeserializeFromString | 1.03 | 57291.56 | 55403.68 | |
System.Text.Json.Serialization.Tests.ReadJson.DeserializeFromStr | 1.03 | 555.44 | 538.91 | |
System.Text.Json.Serialization.Tests.ReadJson.DeserializeFromString | 1.03 | 57411.67 | 55795.76 | |
System.Text.Json.Serialization.Tests.ReadJson.DeserializeFromSt | 1.03 | 57633.80 | 56039.46 | |
System.Text.Json.Serialization.Tests.WriteJson<ImmutableSortedDictionary<String, | 1.03 | 42032.97 | 40874.54 |
No significant change
Same | diff/base | Base Median (ns) | Diff Median (ns) | Modality |
---|---|---|---|---|
System.Text.Json.Serialization.Tests.WriteJson<Stack>.SerializeToString | 1.01 | 13520.86 | 13327.96 | |
System.Text.Json.Serialization.Tests.ReadJson<ICollection>.DeserializeFr | 1.01 | 11715.28 | 11565.19 | |
System.Text.Json.Serialization.Tests.ReadJson.DeserializeFromString | 1.01 | 1406.12 | 1388.53 | |
System.Text.Json.Serialization.Tests.ReadJson.Deseriali | 1.01 | 405604.72 | 401985.10 | |
System.Text.Json.Serialization.Tests.WriteJson<ImmutableDictionary<String, Strin | 1.01 | 51750.93 | 51332.06 | |
System.Text.Json.Serialization.Tests.WriteJson.SerializeToString | 1.01 | 427.78 | 424.38 | |
System.Text.Json.Serialization.Tests.WriteJson.SerializeToString | 1.01 | 1018.08 | 1010.95 | |
System.Text.Json.Serialization.Tests.WriteJson<List>.SerializeToString | 1.01 | 13306.19 | 13220.26 | |
System.Text.Json.Serialization.Tests.WriteJson.SerializeToString | 1.01 | 19018.37 | 18904.12 | |
System.Text.Json.Serialization.Tests.ReadJson<ImmutableArray>.Deserializ | 1.01 | 14556.76 | 14753.08 | |
System.Text.Json.Serialization.Tests.ReadJson<ISet>.DeserializeFromStrin | 1.01 | 18021.83 | 18241.62 | |
System.Text.Json.Serialization.Tests.WriteJson.Seriali | 1.01 | 12987.11 | 13123.09 | |
System.Text.Json.Serialization.Tests.WriteJson<IImmutableList>.Serialize | 1.01 | 19581.13 | 19749.45 | |
System.Text.Json.Serialization.Tests.ReadJson<List>.DeserializeFromStrin | 1.01 | 11689.88 | 11785.58 | |
System.Text.Json.Serialization.Tests.WriteJson.SerializeToStrin | 1.01 | 19399.50 | 19551.79 | |
System.Text.Json.Serialization.Tests.ReadJson<ImmutableDictionary<String, String | 1.01 | 63694.96 | 64026.68 | |
System.Text.Json.Serialization.Tests.ReadJson.DeserializeF | 1.00 | 11780.75 | 11825.79 | |
System.Text.Json.Serialization.Tests.WriteJson.SerializeToString | 1.02 | 19278.72 | 18832.19 | |
System.Text.Json.Serialization.Tests.WriteJson<ISet>.SerializeToString | 1.02 | 13610.87 | 13376.28 | |
System.Text.Json.Serialization.Tests.WriteJson.SerializeTo | 1.02 | 13335.29 | 13113.11 | |
System.Text.Json.Serialization.Tests.ReadJson<IList>.DeserializeFromStri | 1.02 | 11499.77 | 11770.64 | |
System.Text.Json.Serialization.Tests.WriteJson<ICollection>.SerializeToS | 1.02 | 13342.26 | 13614.54 | |
System.Text.Json.Serialization.Tests.WriteJson<HashSet>.SerializeToStrin | 1.02 | 13560.58 | 13793.01 | |
System.Text.Json.Serialization.Tests.WriteJson. | 1.02 | 22324.25 | 22683.23 | |
System.Text.Json.Serialization.Tests.ReadJson.DeserializeFromStr | 1.02 | 35036.74 | 35586.78 |
Slower
Slower | diff/base | Base Median (ns) | Diff Median (ns) | Modality |
---|---|---|---|---|
System.Text.Json.Serialization.Tests.ReadJson.DeserializeFromString | 1.04 | 94277.78 | 98155.60 | |
System.Text.Json.Serialization.Tests.WriteJson<LinkedList>.SerializeToSt | 1.04 | 13257.40 | 13735.20 | |
System.Text.Json.Serialization.Tests.WriteJson<ImmutableArray>.Serialize | 1.03 | 12529.79 | 12863.22 |
...xt.Json/src/System/Text/Json/Serialization/Converters/DefaultImmutableDictionaryConverter.cs
Outdated
Show resolved
Hide resolved
...xt.Json/src/System/Text/Json/Serialization/Converters/DefaultImmutableEnumerableConverter.cs
Outdated
Show resolved
Hide resolved
src/System.Text.Json/src/System/Text/Json/Serialization/ReadStackFrame.cs
Outdated
Show resolved
Hide resolved
src/System.Text.Json/src/System/Text/Json/Serialization/ReadStackFrame.cs
Outdated
Show resolved
Hide resolved
...xt.Json/src/System/Text/Json/Serialization/Converters/DefaultImmutableDictionaryConverter.cs
Outdated
Show resolved
Hide resolved
...xt.Json/src/System/Text/Json/Serialization/Converters/DefaultImmutableEnumerableConverter.cs
Show resolved
Hide resolved
src/System.Text.Json/src/System/Text/Json/Serialization/JsonClassInfo.AddProperty.cs
Show resolved
Hide resolved
src/System.Text.Json/src/System/Text/Json/Serialization/JsonClassInfo.AddProperty.cs
Show resolved
Hide resolved
src/System.Text.Json/src/System/Text/Json/Serialization/JsonClassInfo.AddProperty.cs
Outdated
Show resolved
Hide resolved
src/System.Text.Json/tests/NewtonsoftTests/DateTimeConverterTests.cs
Outdated
Show resolved
Hide resolved
src/System.Text.Json/tests/Serialization/Value.ReadTests.GenericCollections.cs
Outdated
Show resolved
Hide resolved
src/System.Text.Json/tests/Serialization/Value.ReadTests.GenericCollections.cs
Outdated
Show resolved
Hide resolved
src/System.Text.Json/src/System/Text/Json/Serialization/JsonClassInfo.cs
Outdated
Show resolved
Hide resolved
src/System.Text.Json/src/System/Text/Json/Serialization/JsonClassInfo.cs
Outdated
Show resolved
Hide resolved
src/System.Text.Json/src/System/Text/Json/Serialization/JsonClassInfo.cs
Outdated
Show resolved
Hide resolved
src/System.Text.Json/tests/Serialization/Value.ReadTests.NonGenericCollections.cs
Show resolved
Hide resolved
...xt.Json/src/System/Text/Json/Serialization/Converters/DefaultImmutableDictionaryConverter.cs
Show resolved
Hide resolved
@layomia per #41482 I didn't realize So support non-generic Stack the same (broken) in this PR for consistency with We need to consider what to do for 5.0 then per that issue - add an option to have "correct" Stack semantics for example or have a breaking change (probably not feasible) |
) * Populate non-immutable collections directly on deserialize (rather than storing in temporary collections and using converters to create and populate instances) * Fix deserilizing nested dictionaries * Don't get add method for types we can populate without reflection; fix failing tests * Misc perf changes * Additional changes * More changes * Address review feedback * Address feedback
…refx#41482) * Populate non-immutable collections directly on deserialize (rather than storing in temporary collections and using converters to create and populate instances) * Fix deserilizing nested dictionaries * Don't get add method for types we can populate without reflection; fix failing tests * Misc perf changes * Additional changes * More changes * Address review feedback * Address feedback Commit migrated from dotnet/corefx@2c06259
This PR improves performance collections previously deserialized with the use of converters. This includes types derived from native (System.Collections[.Generic]) collections, and native collections that don't implement
ILIst
,IDictionary
, orIDictionary<>
. See #41482 (comment) for the improvements.The deserialization logic for collections is as follows:
Fixes https://github.com/dotnet/corefx/issues/41427 - higher order inheritance for derived types is now supported
Fixes https://github.com/dotnet/corefx/issues/40479 - reflection is now used to support more types.
Fixes https://github.com/dotnet/corefx/issues/41034 - types that implement IDictionary are now supported for serialization.