Skip to content

Commit

Permalink
Performance improvements on property lookup (dotnet/corefx#38902)
Browse files Browse the repository at this point in the history
Commit migrated from dotnet/corefx@5e6c939
  • Loading branch information
steveharter authored Jun 26, 2019
1 parent 1701487 commit 111da13
Show file tree
Hide file tree
Showing 15 changed files with 222 additions and 239 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,18 @@ namespace System.Text.Json
{
internal partial class JsonClassInfo
{
private JsonPropertyInfo AddPolicyProperty(Type propertyType, JsonSerializerOptions options)
private void AddPolicyProperty(Type propertyType, JsonSerializerOptions options)
{
// A policy property is not a real property on a type; instead it leverages the existing converter
// logic and generic support to avoid boxing. It is used with values types and elements from collections and
// dictionaries. Typically it would represent a CLR type such as System.String.
return AddProperty(
PolicyProperty = AddProperty(
propertyType,
propertyInfo : null, // Not a real property so this is null.
classType : typeof(object), // A dummy type (not used).
options : options);

}

private JsonPropertyInfo AddProperty(Type propertyType, PropertyInfo propertyInfo, Type classType, JsonSerializerOptions options)
{
JsonPropertyInfo jsonInfo = CreateProperty(propertyType, propertyType, propertyInfo, classType, options);
Expand Down Expand Up @@ -54,16 +54,6 @@ private JsonPropertyInfo AddProperty(Type propertyType, PropertyInfo propertyInf
}
}

if (propertyInfo != null)
{
_propertyRefs.Add(new PropertyRef(GetKey(jsonInfo.NameUsedToCompare), jsonInfo));
}
else
{
// A single property or an IEnumerable
_propertyRefs.Add(new PropertyRef(0, jsonInfo));
}

return jsonInfo;
}

Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -30,21 +30,14 @@ internal abstract class JsonPropertyInfo

public ClassType ClassType;

// After the property is added, clear any state not used later.
public void ClearUnusedValuesAfterAdd()
{
NameAsString = null;
NameUsedToCompareAsString = null;
}

public abstract JsonConverter ConverterBase { get; set; }

// Copy any settings defined at run-time to the new property.
public void CopyRuntimeSettingsTo(JsonPropertyInfo other)
{
other.Name = Name;
other.NameUsedToCompare = NameUsedToCompare;
other.EscapedName = EscapedName;
other.Name = Name;
other.PropertyNameKey = PropertyNameKey;
}

public abstract IList CreateConverterList();
Expand Down Expand Up @@ -112,20 +105,11 @@ private void DeterminePropertyName()
// At this point propertyName is valid UTF16, so just call the simple UTF16->UTF8 encoder.
Name = Encoding.UTF8.GetBytes(NameAsString);

// Set the compare name.
if (Options.PropertyNameCaseInsensitive)
{
NameUsedToCompareAsString = NameAsString.ToUpperInvariant();
NameUsedToCompare = Encoding.UTF8.GetBytes(NameUsedToCompareAsString);
}
else
{
NameUsedToCompareAsString = NameAsString;
NameUsedToCompare = Name;
}

// Cache the escaped name.
EscapedName = JsonEncodedText.Encode(Name);

ulong key = JsonClassInfo.GetKey(Name);
PropertyNameKey = key;
}

private void DetermineSerializationCapabilities()
Expand Down Expand Up @@ -311,9 +295,8 @@ public virtual void Initialize(
public byte[] Name { get; private set; }
public string NameAsString { get; private set; }

// Used to support case-insensitive comparison
public byte[] NameUsedToCompare { get; private set; }
public string NameUsedToCompareAsString { get; private set; }
// Key for fast property name lookup.
public ulong PropertyNameKey { get; set; }

// Options can be referenced here since all JsonPropertyInfos originate from a JsonClassInfo that is cached on JsonSerializerOptions.
protected JsonSerializerOptions Options { get; set; }
Expand All @@ -335,7 +318,7 @@ public void Read(JsonTokenType tokenType, ref ReadStack state, ref Utf8JsonReade
if (ElementClassInfo != null)
{
// Forward the setter to the value-based JsonPropertyInfo.
JsonPropertyInfo propertyInfo = ElementClassInfo.GetPolicyProperty();
JsonPropertyInfo propertyInfo = ElementClassInfo.PolicyProperty;
propertyInfo.ReadEnumerable(tokenType, ref state, ref reader);
}
else
Expand Down Expand Up @@ -419,10 +402,10 @@ public void Write(ref WriteStack state, Utf8JsonWriter writer)
{
Debug.Assert(ShouldSerialize);

if (state.Current.Enumerator != null)
if (state.Current.CollectionEnumerator != null)
{
// Forward the setter to the value-based JsonPropertyInfo.
JsonPropertyInfo propertyInfo = ElementClassInfo.GetPolicyProperty();
JsonPropertyInfo propertyInfo = ElementClassInfo.PolicyProperty;
propertyInfo.WriteEnumerable(ref state, writer);
}
else
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,17 +104,17 @@ protected override void OnWriteEnumerable(ref WriteStackFrame current, Utf8JsonW
{
if (Converter != null)
{
Debug.Assert(current.Enumerator != null);
Debug.Assert(current.CollectionEnumerator != null);

TConverter value;
if (current.Enumerator is IEnumerator<TConverter> enumerator)
if (current.CollectionEnumerator is IEnumerator<TConverter> enumerator)
{
// Avoid boxing for strongly-typed enumerators such as returned from IList<T>.
value = enumerator.Current;
}
else
{
value = (TConverter)current.Enumerator.Current;
value = (TConverter)current.CollectionEnumerator.Current;
}

if (value == null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,17 +105,17 @@ protected override void OnWriteEnumerable(ref WriteStackFrame current, Utf8JsonW
{
if (Converter != null)
{
Debug.Assert(current.Enumerator != null);
Debug.Assert(current.CollectionEnumerator != null);

TConverter value;
if (current.Enumerator is IEnumerator<TConverter> enumerator)
if (current.CollectionEnumerator is IEnumerator<TConverter> enumerator)
{
// Avoid boxing for strongly-typed enumerators such as returned from IList<T>.
value = enumerator.Current;
}
else
{
value = (TConverter)current.Enumerator.Current;
value = (TConverter)current.CollectionEnumerator.Current;
}

if (value == null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,17 +85,17 @@ protected override void OnWriteEnumerable(ref WriteStackFrame current, Utf8JsonW
{
if (Converter != null)
{
Debug.Assert(current.Enumerator != null);
Debug.Assert(current.CollectionEnumerator != null);

TProperty? value;
if (current.Enumerator is IEnumerator<TProperty?> enumerator)
if (current.CollectionEnumerator is IEnumerator<TProperty?> enumerator)
{
// Avoid boxing for strongly-typed enumerators such as returned from IList<T>.
value = enumerator.Current;
}
else
{
value = (TProperty?)current.Enumerator.Current;
value = (TProperty?)current.CollectionEnumerator.Current;
}

if (value == null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ private static void HandlePropertyName(

if (state.Current.IsDictionary || state.Current.IsIDictionaryConstructible)
{
state.Current.JsonPropertyInfo = state.Current.JsonClassInfo.GetPolicyProperty();
state.Current.JsonPropertyInfo = state.Current.JsonClassInfo.PolicyProperty;
}

Debug.Assert(
Expand All @@ -73,7 +73,7 @@ private static void HandlePropertyName(
propertyName = GetUnescapedString(propertyName, idx);
}

state.Current.JsonPropertyInfo = state.Current.JsonClassInfo.GetProperty(options, propertyName, ref state.Current);
state.Current.JsonPropertyInfo = state.Current.JsonClassInfo.GetProperty(propertyName, ref state.Current);
if (state.Current.JsonPropertyInfo == null)
{
if (state.Current.JsonClassInfo.DataExtensionProperty == null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ private static void ReadCore(
readStack.Push();
readStack.Current.Drain = true;
}
else if (readStack.Current.IsProcessingValue)
else if (readStack.Current.IsProcessingValue(tokenType))
{
if (!HandleObjectAsValue(tokenType, options, ref reader, ref readStack, ref initialState))
{
Expand Down Expand Up @@ -92,7 +92,7 @@ private static void ReadCore(
}
else if (tokenType == JsonTokenType.StartArray)
{
if (!readStack.Current.IsProcessingValue)
if (!readStack.Current.IsProcessingValue(tokenType))
{
HandleStartArray(options, ref reader, ref readStack);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ private static bool HandleDictionary(
ref WriteStack state)
{
JsonPropertyInfo jsonPropertyInfo = state.Current.JsonPropertyInfo;
if (state.Current.Enumerator == null)
if (state.Current.CollectionEnumerator == null)
{
IEnumerable enumerable;

Expand All @@ -36,37 +36,37 @@ private static bool HandleDictionary(

if (enumerable is IDictionary dictionary)
{
state.Current.Enumerator = dictionary.GetEnumerator();
state.Current.CollectionEnumerator = dictionary.GetEnumerator();
}
else
{
state.Current.Enumerator = enumerable.GetEnumerator();
state.Current.CollectionEnumerator = enumerable.GetEnumerator();
}

state.Current.WriteObjectOrArrayStart(ClassType.Dictionary, writer);
}

if (state.Current.Enumerator.MoveNext())
if (state.Current.CollectionEnumerator.MoveNext())
{
// Check for polymorphism.
if (elementClassInfo.ClassType == ClassType.Unknown)
{
object currentValue = ((IDictionaryEnumerator)state.Current.Enumerator).Entry.Value;
object currentValue = ((IDictionaryEnumerator)state.Current.CollectionEnumerator).Entry.Value;
GetRuntimeClassInfo(currentValue, ref elementClassInfo, options);
}

if (elementClassInfo.ClassType == ClassType.Value)
{
elementClassInfo.GetPolicyProperty().WriteDictionary(ref state, writer);
elementClassInfo.PolicyProperty.WriteDictionary(ref state, writer);
}
else if (state.Current.Enumerator.Current == null)
else if (state.Current.CollectionEnumerator.Current == null)
{
writer.WriteNull(jsonPropertyInfo.Name);
}
else
{
// An object or another enumerator requires a new stack frame.
var enumerator = (IDictionaryEnumerator)state.Current.Enumerator;
var enumerator = (IDictionaryEnumerator)state.Current.CollectionEnumerator;
object value = enumerator.Value;
state.Push(elementClassInfo, value);
state.Current.KeyName = (string)enumerator.Key;
Expand All @@ -78,7 +78,7 @@ private static bool HandleDictionary(
// We are done enumerating.
writer.WriteEndObject();

if (state.Current.PopStackOnEnd)
if (state.Current.PopStackOnEndCollection)
{
state.Pop();
}
Expand All @@ -101,25 +101,25 @@ internal static void WriteDictionary<TProperty>(
return;
}

Debug.Assert(current.Enumerator != null);
Debug.Assert(current.CollectionEnumerator != null);

string key;
TProperty value;
if (current.Enumerator is IEnumerator<KeyValuePair<string, TProperty>> enumerator)
if (current.CollectionEnumerator is IEnumerator<KeyValuePair<string, TProperty>> enumerator)
{
// Avoid boxing for strongly-typed enumerators such as returned from IDictionary<string, TRuntimeProperty>
value = enumerator.Current.Value;
key = enumerator.Current.Key;
}
else if (current.Enumerator is IEnumerator<KeyValuePair<string, object>> polymorphicEnumerator)
else if (current.CollectionEnumerator is IEnumerator<KeyValuePair<string, object>> polymorphicEnumerator)
{
value = (TProperty)polymorphicEnumerator.Current.Value;
key = polymorphicEnumerator.Current.Key;
}
else if (current.IsIDictionaryConstructible || current.IsIDictionaryConstructibleProperty)
{
value = (TProperty)((DictionaryEntry)current.Enumerator.Current).Value;
key = (string)((DictionaryEntry)current.Enumerator.Current).Key;
value = (TProperty)((DictionaryEntry)current.CollectionEnumerator.Current).Value;
key = (string)((DictionaryEntry)current.CollectionEnumerator.Current).Key;
}
else
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ private static bool HandleEnumerable(
{
Debug.Assert(state.Current.JsonPropertyInfo.ClassType == ClassType.Enumerable);

if (state.Current.Enumerator == null)
if (state.Current.CollectionEnumerator == null)
{
IEnumerable enumerable = (IEnumerable)state.Current.JsonPropertyInfo.GetValueAsObject(state.Current.CurrentValue);

Expand All @@ -32,33 +32,33 @@ private static bool HandleEnumerable(
return true;
}

state.Current.Enumerator = enumerable.GetEnumerator();
state.Current.CollectionEnumerator = enumerable.GetEnumerator();

state.Current.WriteObjectOrArrayStart(ClassType.Enumerable, writer);
}

if (state.Current.Enumerator.MoveNext())
if (state.Current.CollectionEnumerator.MoveNext())
{
// Check for polymorphism.
if (elementClassInfo.ClassType == ClassType.Unknown)
{
object currentValue = state.Current.Enumerator.Current;
object currentValue = state.Current.CollectionEnumerator.Current;
GetRuntimeClassInfo(currentValue, ref elementClassInfo, options);
}

if (elementClassInfo.ClassType == ClassType.Value)
{
elementClassInfo.GetPolicyProperty().WriteEnumerable(ref state, writer);
elementClassInfo.PolicyProperty.WriteEnumerable(ref state, writer);
}
else if (state.Current.Enumerator.Current == null)
else if (state.Current.CollectionEnumerator.Current == null)
{
// Write a null object or enumerable.
writer.WriteNullValue();
}
else
{
// An object or another enumerator requires a new stack frame.
object nextValue = state.Current.Enumerator.Current;
object nextValue = state.Current.CollectionEnumerator.Current;
state.Push(elementClassInfo, nextValue);
}

Expand All @@ -68,7 +68,7 @@ private static bool HandleEnumerable(
// We are done enumerating.
writer.WriteEndArray();

if (state.Current.PopStackOnEnd)
if (state.Current.PopStackOnEndCollection)
{
state.Pop();
}
Expand Down
Loading

0 comments on commit 111da13

Please sign in to comment.