Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Improve throughput / allocations of JsonNode.GetPath#92284

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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
stephentoub merged 3 commits intodotnet:mainfromstephentoub:vsbgetpath
Oct 3, 2023
Merged
Show file tree
Hide file tree
Changes from1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
NextNext commit
Improve throughput / allocations of JsonNode.GetPath
The current implementation is creating a `List<string>` and appending each segment to it, which in most of the cases is allocating a `string`. Then it iterates through that list in reverse order appending to a newly-created `StringBuilder`, which it then `ToString`s. In this change, it instead just uses `ValueStringBuilder`, appending to it as it goes.In doing so, it does reverse the order of enumeration. Previously each node would effectively do:```C#void GetPath(){    AddNode(this);    parent?.GetPath();}```and now it's doing:```C#void GetPath(){    parent?.GetPath();    AddNode(this);}```While C# doesn't emit tail calls, with optimizations enabled, it's feasible the JIT might emit the recursive call as a jmp rather than a call, in which case it would avoid possible stack dives. However, that's not guaranteed, and doesn't happen today in tier 0 and other unoptimized code. On top of that, to get such a deep nesting in a JsonNode, you need to either go out of your way to create one manually using the JsonNode/Object/Array/Value constructors, or you need to use JsonSerializer.Deserializer, overriding its default MaxDepth, and in the case of a really deep input, it's also recursive and will stack overflow in smaller situations. I have a different version of this change that keeps the same ordering, passing around a span and a length separately, and prepending to the end of the span, but it results in more complicated code, so I'd prefer this variation that just uses ValueStringBuilder unless we have real concerns.
  • Loading branch information
@stephentoub
stephentoub committedSep 19, 2023
commitf838a8417bd8795c3e13cad39d42d055e10c37a3
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -251,7 +251,7 @@ public unsafe void Append(char* value, int length)
_pos += length;
}

public void Append(ReadOnlySpan<char> value)
public void Append(scopedReadOnlySpan<char> value)
{
int pos = _pos;
if (pos > _chars.Length - value.Length)
Expand Down
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -24,6 +24,7 @@ The System.Text.Json library is built-in as part of the shared framework in .NET

<ItemGroup>
<Compile Include="$(CommonPath)System\HexConverter.cs" Link="Common\System\HexConverter.cs" />
<Compile Include="$(CommonPath)System\Text\ValueStringBuilder.cs" Link="Common\ValueStringBuilder.cs" />
<Compile Include="$(CommonPath)System\Text\Json\PooledByteBufferWriter.cs" Link="Common\System\Text\Json\PooledByteBufferWriter.cs" />
<Compile Include="..\Common\JsonCamelCaseNamingPolicy.cs" Link="Common\System\Text\Json\JsonCamelCaseNamingPolicy.cs" />
<Compile Include="..\Common\JsonNamingPolicy.cs" Link="Common\System\Text\Json\JsonNamingPolicy.cs" />
Expand Down
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -74,6 +74,7 @@ internal static partial class JsonConstants

public const int MaximumFormatBooleanLength = 5;
public const int MaximumFormatInt64Length = 20; // 19 + sign (i.e. -9223372036854775808)
public const int MaximumFormatUInt32Length = 10; // i.e. 4294967295
public const int MaximumFormatUInt64Length = 20; // i.e. 18446744073709551615
public const int MaximumFormatDoubleLength = 128; // default (i.e. 'G'), using 128 (rather than say 32) to be future-proof.
public const int MaximumFormatSingleLength = 128; // default (i.e. 'G'), using 128 (rather than say 32) to be future-proof.
Expand Down
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -202,15 +202,25 @@ internal void SetItem(int index, JsonNode? value)
List[index] = value;
}

internal override void GetPath(List<string> path, JsonNode? child)
internal override void GetPath(ref ValueStringBuilder path, JsonNode? child)
{
Parent?.GetPath(ref path, this);

if (child != null)
{
int index = List.IndexOf(child);
path.Add($"[{index}]");
Debug.Assert(index >= 0);

path.Append('[');
#if NETCOREAPP
Span<char> chars = stackalloc char[JsonConstants.MaximumFormatUInt32Length];
((uint)index).TryFormat(chars, out int charsWritten);
path.Append(chars.Slice(0, charsWritten));
#else
path.Append(index.ToString());
#endif
path.Append(']');
}

Parent?.GetPath(path, this);
}

/// <inheritdoc/>
Expand Down
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -131,19 +131,13 @@ public string GetPath()
return "$";
}

var path = new List<string>();
GetPath(path, null);

var sb = new StringBuilder("$");
for (int i = path.Count - 1; i >= 0; i--)
{
sb.Append(path[i]);
}

return sb.ToString();
var path = new ValueStringBuilder(stackalloc char[JsonConstants.StackallocCharThreshold]);
path.Append('$');
GetPath(ref path, null);
return path.ToString();
}

internal abstract void GetPath(List<string> path, JsonNode? child);
internal abstract void GetPath(ref ValueStringBuilder path, JsonNode? child);

/// <summary>
/// Gets the root <see cref="JsonNode"/>.
Expand Down
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -196,22 +196,25 @@ internal override bool DeepEqualsCore(JsonNode? node)
return null;
}

internal override void GetPath(List<string> path, JsonNode? child)
internal override void GetPath(ref ValueStringBuilder path, JsonNode? child)
{
Parent?.GetPath(ref path, this);

if (child != null)
{
string propertyName = Dictionary.FindValue(child)!.Value.Key;
if (propertyName.AsSpan().ContainsSpecialCharacters())
{
path.Add($"['{propertyName}']");
path.Append("['");
path.Append(propertyName);
path.Append("']");
}
else
{
path.Add($".{propertyName}");
path.Append('.');
path.Append(propertyName);
}
}

Parent?.GetPath(path, this);
}

internal void SetItem(string propertyName, JsonNode? value)
Expand Down
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -90,11 +90,11 @@ private protected JsonValue(JsonNodeOptions? options = null) : base(options) { }
return new JsonValueCustomized<T>(value, jsonTypeInfo, options);
}

internal override void GetPath(List<string> path, JsonNode? child)
internal override void GetPath(ref ValueStringBuilder path, JsonNode? child)
{
Debug.Assert(child == null);

Parent?.GetPath(path, this);
Parent?.GetPath(refpath, this);
}

/// <summary>
Expand Down

[8]ページ先頭

©2009-2025 Movatter.jp