Serialization performance improvements#41098
Conversation
|
|
||
| string result; | ||
|
|
||
| using (var output = new PooledByteBufferWriter(options.DefaultBufferSize)) |
There was a problem hiding this comment.
The return value was not used, so this code removed.
There was a problem hiding this comment.
So, this was just pure overhead that was redundant/unnecessary work?
| private const int PropertyNameCountCacheThreshold = 64; | ||
|
|
||
| // All of the serializable properties on a POCO keyed on property name. | ||
| // All of the serializable properties on a POCO (except the optional extension property) keyed on property name. |
There was a problem hiding this comment.
By extension, do you mean JsonExtensionData?
| cacheArray = new JsonPropertyInfo[cache.Count + 1]; | ||
|
|
||
| // Set the last element to the extension property. | ||
| cacheArray[cache.Count] = DataExtensionProperty; |
There was a problem hiding this comment.
It avoids an extra call to HandleObject() which is now inlined (so we only have one call now, instead of two).
FYI the dictionary-based cache doesn't (and can't) contain the extension property since it is used during deserialization to match property names which we don't want to do (it is not possible to deserialize directly into the extension property - only indirectly through a missing property)
| current.JsonPropertyInfo.Write(ref state, writer); | ||
| finishedSerializing = true; | ||
| break; | ||
| case ClassType.Object: |
There was a problem hiding this comment.
Is ClassType.Object ever used? And was this code always unreachable?
There was a problem hiding this comment.
Yes it is used -- if type of a property is System.Object then all properties start out with ClassType.Object and then are converted at run-time to the instance's type (via obj.GetType()). So at this point in the code, ClassType.Object is only used when the instance is System.Object (an edge case for sure).
|
|
||
| if (finishedSerializing) | ||
| { | ||
| if (writer.CurrentDepth == 0 || writer.CurrentDepth == originalWriterDepth) |
There was a problem hiding this comment.
Is this a valid change? What happens if the writer is at depth X, and then the serializer writes a bunch of things that puts the writer at depth 0? Is that scenario possible?
There was a problem hiding this comment.
The serializer should never go below the original which is determined from the serializer's entry point (e.g. Serialize()). Currently originalWriterDepth is 0 for the normal case and X for re-entry cases when a writer is passed into the Serialize() method.
| int len = JsonClassInfo.PropertyCacheArray.Length; | ||
| if (PropertyEnumeratorIndex < len) | ||
| { | ||
| if ((PropertyEnumeratorIndex == len - 1) && JsonClassInfo.DataExtensionProperty != null) |
There was a problem hiding this comment.
How's the code coverage? Are these branches covered?
There was a problem hiding this comment.
This is hit 100% of the time when we can an extension property.
There was a problem hiding this comment.
There was a problem hiding this comment.
Thanks. I will send a new PR to clean this up.
| @@ -72,11 +67,13 @@ private static bool WriteEndObject(ref WriteStack state) | |||
| return true; | |||
There was a problem hiding this comment.
Code path in line 62 is not really needed, it is only hit when root object finishes and just sets root's WriteStackFrame properties to null/false, which doesn't make any difference at the end of serialization.
@steveharter
There was a problem hiding this comment.
Yes thanks. It is not needed - any future push() from a subsequent serialization will reset the state.
Commit migrated from dotnet/corefx@017a038

Serialization improvements from 15%-28% on existing benchmarks (for simple objects).
Changes in order of most impact to lesser impact:
[AggressiveInlining]when processing property values. Required some misc refactoring so that the methods inlined are only called once (instead of twice).JsonEncodedTest) to avoid a copy on read.WriteJson benchmarks
Comparisons to Json.NET
MyEventsListerViewModel Before
MyEventsListerViewModel After
IndexViewModel Before
IndexViewModel After
Location Before
Location After
LoginViewModel Before
LoginViewModel After