-
Notifications
You must be signed in to change notification settings - Fork 4.8k
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
Fix JsonNode performance regression #104048
Conversation
Tagging subscribers to this area: @dotnet/area-system-text-json, @gregsdennis |
@@ -73,7 +73,7 @@ public override void Write(Utf8JsonWriter writer, JsonNode? value, JsonSerialize | |||
node = new JsonArray(element, options); | |||
break; | |||
default: | |||
node = JsonValue.CreateFromElement(ref element, options); | |||
node = new JsonValueOfElement(element, options); |
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.
The change looks good, but I'm a little surprised it caused such regressions. It looks like CreateFromElement is also using this constructor but before that is checking ValueKind against the same three cases above. Those branches were enough to cause significant regressions? This must be a super hot path?
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.
It's in the hot path for the lazy conversion of JsonArray
and JsonObject
from JsonElement
backed to List and dictionary backed, which is what regressed by this change.
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.
I wonder if the ref readonly
parameter in the removed method contributed to the regression somehow.
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.
My guess is that it calls element.ValueKind
twice, which resolves as follows
public JsonValueKind ValueKind => TokenType.ToValueKind();`
private JsonTokenType TokenType => _parent?.GetJsonTokenType(_idx) ?? JsonTokenType.None
internal JsonTokenType GetJsonTokenType(int index)
{
CheckNotDisposed();
return _parsedData.GetJsonTokenType(index);
}
internal JsonTokenType GetJsonTokenType(int index)
{
AssertValidIndex(index);
uint union = MemoryMarshal.Read<uint>(_data.AsSpan(index + NumberOfRowsOffset));
return (JsonTokenType)(union >> 28);
}
plus a small cost to run the switch statement in internal static JsonValueKind ToValueKind(this JsonTokenType tokenType)
Which is quite a lot of indirection. Possibly changing CreateFromElement
to create a local JsonValueKind kind = element.ValueKind;
to avoid running the above twice? But still maybe running it once is even a lot here.
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.
Created a PR here #104108
Fixes dotnet/perf-autofiling-issues#36936 (comment)