-
Notifications
You must be signed in to change notification settings - Fork 281
PERF: Reduce boxing allocations in GetScalarValue #2938
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
base: main
Are you sure you want to change the base?
Changes from 2 commits
73d9648
9bdea50
9e7ad1c
512cd81
f120c66
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,10 +1,11 @@ | ||
| // Copyright (c) Microsoft Corporation. All rights reserved. | ||
| // Copyright (c) Microsoft Corporation. All rights reserved. | ||
| // Licensed under the MIT license. | ||
|
|
||
| using System; | ||
| using System.Collections.Generic; | ||
| using System.Globalization; | ||
| using System.Linq; | ||
| using System.Text.Json; | ||
| using System.Text.Json.Nodes; | ||
|
|
||
| namespace Microsoft.OpenApi.Reader | ||
|
|
@@ -159,9 +160,63 @@ public static Dictionary<string, HashSet<T>> CreateArrayMap<T>(this JsonNode? no | |
| { | ||
| var scalarNode = node is JsonValue value ? value : throw new OpenApiException("Expected scalar value."); | ||
|
|
||
| // It's much more efficient to call scalarNode.TryGetValue<string> than to call scalarNode.GetValue<object>() and then convert to string. | ||
| // When asking for "object" type, if scalarNode is JsonValueOfElement (internal type in STJ), we will get back | ||
| // a boxed JsonElement (paying the cost of unnecessary boxing allocation), and we then call JsonElement.ToString. | ||
| // So, we first check if we can get the string value directly, and only if that fails, we fallback to the expensive code. | ||
| if (scalarNode.TryGetValue<string>(out var stringValue)) | ||
| { | ||
| return stringValue; | ||
| } | ||
|
|
||
| return Convert.ToString(scalarNode.GetValue<object>(), CultureInfo.InvariantCulture); | ||
| } | ||
|
|
||
| public static bool GetScalarBoolValue(this JsonNode? node) | ||
| { | ||
| var scalarNode = node is JsonValue value ? value : throw new OpenApiException("Expected scalar value."); | ||
| return scalarNode.GetValue<bool>(); | ||
| } | ||
|
Comment on lines
+175
to
+179
|
||
|
|
||
| public static int GetScalarIntValue(this JsonNode? node) | ||
| { | ||
| var scalarNode = node is JsonValue value && value.GetValueKind() == JsonValueKind.Number ? value : throw new OpenApiException("Expected numeric scalar value."); | ||
|
Youssef1313 marked this conversation as resolved.
|
||
|
|
||
| if (scalarNode.TryGetValue<int>(out var intValue)) | ||
| { | ||
| return intValue; | ||
| } | ||
| else if (scalarNode.TryGetValue<long>(out var longValue)) | ||
| { | ||
| return (int)longValue; | ||
| } | ||
| else if (scalarNode.TryGetValue<decimal>(out var decimalValue)) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can we get other numeric types?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Decimal in most cases will be coming through the YML library being used to convert YML to JSON. Edit: It's not the library. It's our code to handle YML. I tried to adjust it so that it handles numeric in a more correct way. |
||
| { | ||
| return (int)decimalValue; | ||
| } | ||
|
|
||
| return Convert.ToInt32(scalarNode.GetValue<object>()); | ||
| } | ||
|
Youssef1313 marked this conversation as resolved.
Comment on lines
+181
to
+191
|
||
|
|
||
| public static uint GetScalarUIntValue(this JsonNode? node) | ||
| { | ||
| var scalarNode = node is JsonValue value && value.GetValueKind() == JsonValueKind.Number ? value : throw new OpenApiException("Expected numeric scalar value."); | ||
| if (scalarNode.TryGetValue<uint>(out var uintValue)) | ||
| { | ||
| return uintValue; | ||
| } | ||
| else if (scalarNode.TryGetValue<ulong>(out var ulongValue)) | ||
| { | ||
| return (uint)ulongValue; | ||
| } | ||
| else if (scalarNode.TryGetValue<decimal>(out var decimalValue)) | ||
| { | ||
| return (uint)decimalValue; | ||
| } | ||
|
|
||
| return Convert.ToUInt32(scalarNode.GetValue<object>()); | ||
| } | ||
|
Youssef1313 marked this conversation as resolved.
Comment on lines
+193
to
+202
|
||
|
|
||
| public static string? GetReferencePointer(this JsonObject jsonObject) | ||
| { | ||
| return jsonObject.TryGetPropertyValue("$ref", out var refNode) ? refNode?.GetScalarValue() : null; | ||
|
|
||
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 think that one reason why we had to do that was to avoid mangling like default, numbers with formats, etc... While I do appreciate the performance improvements, I don't want it to come at the price of a regression.
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.
TryGetValue<string>is never going to throw or do something strange. It will return true if the node really represents a string. For example, if you have any numbers in JSON like"maximum": 10,TryGetValue<string>will return false because the node doesn't represent a string. And then we fallback to the original implementation.I done it this way explicitly because I saw that some numerics are read raw as strings that way.
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.
This is what I'm referring to c17a87e
I think the object convert was introduced to avoid formatting issues for defaults and other properties when using types like dates and such.