Skip to content

Commit 2d95c7e

Browse files
authored
Add RFC benchmark and fix minor bugs (#21)
* Add rfc benchmarking * Fix docs and minor bugs * Update benchmark and docs
1 parent e09e667 commit 2d95c7e

24 files changed

+303
-304
lines changed

Benchmark.md

Lines changed: 41 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
# Benchmarks
22

3-
## Benchmarks with different options
4-
5-
_All benchmarks are generated using the same small JSON object used in the **Newtonsoft Json vs System.Text.Json** section below._
3+
## Hardware and Software
64

75
``` ini
86

@@ -14,78 +12,56 @@ BenchmarkDotNet=v0.13.1, OS=Windows 10.0.19043.1645 (21H1/May2021Update)
1412

1513

1614
```
17-
| Method | Mean | Min | Max | P80 | P95 | Allocated |
18-
|--------- |---------:|---------:|---------:|---------:|---------:|----------:|
19-
| RawText | 112.0 μs | 109.2 μs | 115.2 μs | 112.8 μs | 114.2 μs | 77 KB |
20-
| Semantic | 116.1 μs | 113.8 μs | 121.7 μs | 117.0 μs | 119.3 μs | 76 KB |
21-
2215

23-
## Newtonsoft Json vs System.Text.Json
16+
## Comparison Modes
2417

25-
_All benchmarks for `*_SystemTextJson` methods are generated with `JsonElementComparison.Semantic` option._
18+
| Method | FileSize | Mean | Median | Min | Max | P80 | P95 | Allocated |
19+
|--------- |--------- |---------:|---------:|---------:|---------:|---------:|---------:|----------:|
20+
| RawText | Small | 100.9 μs | 100.3 μs | 98.36 μs | 105.7 μs | 102.0 μs | 104.9 μs | 77 KB |
21+
| Semantic | Small | 102.2 μs | 101.9 μs | 99.37 μs | 107.0 μs | 103.1 μs | 105.6 μs | 76 KB |
2622

27-
### Small JSON object
28-
29-
``` ini
23+
\* _All benchmarks are generated using the same small JSON object used in the **System.Text.Json vs Newtonsoft Json** section below, with array move detection enabled (default)._
3024

31-
BenchmarkDotNet=v0.13.1, OS=Windows 10.0.19043.1645 (21H1/May2021Update)
32-
11th Gen Intel Core i7-1185G7 3.00GHz, 1 CPU, 8 logical and 4 physical cores
33-
.NET SDK=6.0.200
34-
[Host] : .NET 6.0.2 (6.0.222.6406), X64 RyuJIT
35-
Job-HEQUNO : .NET 6.0.2 (6.0.222.6406), X64 RyuJIT
25+
## System.Text.Json vs Newtonsoft Json
3626

27+
### Diff (including RFC JsonPatch)
3728

38-
```
39-
| Method | Mean | Min | Max | P80 | P95 | Allocated |
40-
|-------------------------- |----------:|----------:|----------:|----------:|----------:|----------:|
41-
| Diff_JsonNet | 109.05 μs | 105.82 μs | 119.70 μs | 111.81 μs | 115.36 μs | 132 KB |
42-
| Diff_SystemTextJson | 88.99 μs | 87.03 μs | 90.95 μs | 89.82 μs | 90.78 μs | 66 KB |
43-
| Patch_JsonNet | 115.06 μs | 112.71 μs | 126.55 μs | 115.31 μs | 117.95 μs | 162 KB |
44-
| Patch_SystemTextJson | 41.39 μs | 40.67 μs | 42.67 μs | 41.62 μs | 42.03 μs | 35 KB |
45-
| DeepEquals_JsonNet | 70.90 μs | 68.80 μs | 74.54 μs | 71.31 μs | 72.68 μs | 91 KB |
46-
| DeepEquals_SystemTextJson | 63.46 μs | 61.45 μs | 66.30 μs | 64.26 μs | 64.70 μs | 39 KB |
47-
| DeepClone_JsonNet | 49.64 μs | 48.66 μs | 50.77 μs | 50.05 μs | 50.39 μs | 70 KB |
48-
| DeepClone_SystemTextJson | 34.90 μs | 33.38 μs | 38.59 μs | 36.32 μs | 37.19 μs | 40 KB |
29+
| Method | FileSize | Mean | Median | Min | Max | P80 | P95 | Allocated |
30+
|------------------- |--------- |------------:|------------:|------------:|------------:|------------:|------------:|----------:|
31+
| **SystemTextJson** | **Small** | **79.09 μs** | **78.25 μs** | **75.72 μs** | **84.89 μs** | **81.91 μs** | **84.16 μs** | **66 KB** |
32+
| JsonNet | Small | 91.98 μs | 92.15 μs | 90.06 μs | 94.50 μs | 92.77 μs | 93.78 μs | 132 KB |
33+
| SystemTextJson_Rfc | Small | 94.88 μs | 94.74 μs | 93.34 μs | 97.80 μs | 95.69 μs | 96.86 μs | 87 KB |
34+
| JsonNet_Rfc | Small | 106.41 μs | 106.01 μs | 103.58 μs | 110.69 μs | 107.38 μs | 109.75 μs | 150 KB |
35+
| **SystemTextJson** | **Large** | **3,717.44 μs** | **3,700.53 μs** | **3,577.00 μs** | **3,913.15 μs** | **3,766.22 μs** | **3,901.23 μs** | **3,258 KB** |
36+
| JsonNet | Large | 4,104.18 μs | 4,085.60 μs | 3,922.80 μs | 4,343.10 μs | 4,199.56 μs | 4,273.98 μs | 4,386 KB |
37+
| SystemTextJson_Rfc | Large | 4,900.93 μs | 4,890.86 μs | 4,772.28 μs | 5,128.16 μs | 4,958.30 μs | 5,021.94 μs | 4,561 KB |
38+
| JsonNet_Rfc | Large | 5,569.83 μs | 5,535.12 μs | 5,354.93 μs | 5,976.46 μs | 5,682.46 μs | 5,822.63 μs | 6,147 KB |
4939

40+
### DeepEquals
5041

51-
### Large JSON object
42+
| Method | FileSize | Mean | Median | Min | Max | P80 | P95 | Allocated |
43+
|--------------- |--------- |------------:|------------:|------------:|------------:|------------:|------------:|----------:|
44+
| **SystemTextJson** | **Small** | **52.92 μs** | **52.89 μs** | **52.00 μs** | **54.46 μs** | **53.31 μs** | **53.90 μs** | **39 KB** |
45+
| JsonNet | Small | 58.82 μs | 58.77 μs | 57.78 μs | 60.41 μs | 59.16 μs | 59.74 μs | 91 KB |
46+
| **SystemTextJson** | **Large** | **2,099.55 μs** | **2,090.78 μs** | **1,963.92 μs** | **2,302.56 μs** | **2,161.80 μs** | **2,223.10 μs** | **1,631 KB** |
47+
| JsonNet | Large | 2,296.54 μs | 2,293.76 μs | 2,239.68 μs | 2,393.52 μs | 2,323.09 μs | 2,378.88 μs | 2,426 KB |
5248

53-
``` ini
49+
### Patch
5450

55-
BenchmarkDotNet=v0.13.1, OS=Windows 10.0.19043.1645 (21H1/May2021Update)
56-
11th Gen Intel Core i7-1185G7 3.00GHz, 1 CPU, 8 logical and 4 physical cores
57-
.NET SDK=6.0.200
58-
[Host] : .NET 6.0.2 (6.0.222.6406), X64 RyuJIT
59-
Job-BZNWDS : .NET 6.0.2 (6.0.222.6406), X64 RyuJIT
51+
| Method | FileSize | Mean | Median | Min | Max | P80 | P95 | Allocated |
52+
|--------------- |--------- |------------:|------------:|------------:|------------:|------------:|------------:|----------:|
53+
| **SystemTextJson** | **Small** | **35.45 μs** | **35.42 μs** | **34.43 μs** | **36.97 μs** | **35.86 μs** | **36.52 μs** | **35 KB** |
54+
| JsonNet | Small | 95.50 μs | 95.35 μs | 94.14 μs | 97.36 μs | 96.28 μs | 96.70 μs | 162 KB |
55+
| **SystemTextJson** | **Large** | **1,945.77 μs** | **1,935.61 μs** | **1,799.91 μs** | **2,203.39 μs** | **2,047.02 μs** | **2,093.61 μs** | **1,732 KB** |
56+
| JsonNet | Large | 4,324.16 μs | 4,315.50 μs | 4,184.21 μs | 4,506.67 μs | 4,378.94 μs | 4,433.86 μs | 5,088 KB |
6057

58+
### DeepClone
6159

62-
```
63-
| Method | Mean | Min | Max | P80 | P95 | Allocated |
64-
|-------------------------- |---------:|---------:|---------:|---------:|---------:|----------:|
65-
| Diff_JsonNet | 5.534 ms | 4.986 ms | 6.186 ms | 5.732 ms | 6.008 ms | 4 MB |
66-
| Diff_SystemTextJson | 5.026 ms | 4.725 ms | 5.229 ms | 5.124 ms | 5.216 ms | 3 MB |
67-
| Patch_JsonNet | 5.998 ms | 5.342 ms | 6.634 ms | 6.171 ms | 6.541 ms | 5 MB |
68-
| Patch_SystemTextJson | 2.603 ms | 2.276 ms | 2.965 ms | 2.720 ms | 2.934 ms | 2 MB |
69-
| DeepEquals_JsonNet | 2.986 ms | 2.684 ms | 3.512 ms | 3.252 ms | 3.307 ms | 2 MB |
70-
| DeepEquals_SystemTextJson | 2.461 ms | 2.348 ms | 2.644 ms | 2.514 ms | 2.620 ms | 2 MB |
71-
| DeepClone_JsonNet | 2.029 ms | 1.991 ms | 2.067 ms | 2.047 ms | 2.061 ms | 2 MB |
72-
| DeepClone_SystemTextJson | 1.522 ms | 1.484 ms | 1.576 ms | 1.538 ms | 1.562 ms | 2 MB |
73-
74-
75-
### `DeepEquals`
76-
77-
``` ini
78-
79-
BenchmarkDotNet=v0.13.1, OS=Windows 10.0.19043.1645 (21H1/May2021Update)
80-
11th Gen Intel Core i7-1185G7 3.00GHz, 1 CPU, 8 logical and 4 physical cores
81-
.NET SDK=6.0.200
82-
[Host] : .NET 6.0.2 (6.0.222.6406), X64 RyuJIT
83-
Job-MDGOSR : .NET 6.0.2 (6.0.222.6406), X64 RyuJIT
84-
85-
86-
```
87-
| Method | Mean | Min | Max | P80 | P95 | Allocated |
88-
|--------------- |---------:|---------:|---------:|---------:|---------:|----------:|
89-
| JsonNet | 17.22 μs | 16.87 μs | 17.73 μs | 17.37 μs | 17.48 μs | 23 KB |
90-
| SystemTextJson | 16.76 μs | 16.44 μs | 17.07 μs | 16.87 μs | 16.99 μs | 10 KB |
60+
| Method | FileSize | Mean | Median | Min | Max | P80 | P95 | Allocated |
61+
|--------------- |--------- |------------:|------------:|------------:|------------:|------------:|------------:|----------:|
62+
| **SystemTextJson** | **Small** | **28.98 μs** | **29.05 μs** | **27.99 μs** | **29.53 μs** | **29.29 μs** | **29.42 μs** | **40 KB** |
63+
| JsonNet | Small | 42.99 μs | 42.84 μs | 41.90 μs | 45.02 μs | 43.41 μs | 44.70 μs | 70 KB |
64+
| **SystemTextJson** | **Large** | **1,251.60 μs** | **1,247.97 μs** | **1,192.19 μs** | **1,323.97 μs** | **1,276.05 μs** | **1,310.40 μs** | **1,675 KB** |
65+
| JsonNet | Large | 1,708.43 μs | 1,706.69 μs | 1,664.39 μs | 1,783.04 μs | 1,731.47 μs | 1,759.00 μs | 2,128 KB |
9166

67+
\* _All benchmarks for `SystemTextJson` methods are generated with `JsonElementComparison.Semantic` option and array move detection disabled because JsonDiffPatch.Net does not support array move detection._

ReleaseNotes.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
## 1.2.0
44

5-
- Major performance improvement in array comparion
5+
- Major performance improvement in array comparison
66
- Performance improvements in `DeepEquals` and `JsonValueComparer`
77
- **[BREAKING CHANGE]** Breaking changes in array comparison:
88
- Removed `JsonDiffOptions.PreferFuzzyArrayItemMatch` option

src/Directory.Build.props

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
</PropertyGroup>
1010

1111
<PropertyGroup>
12-
<JsonDiffPatchPackageVersion>1.2.0</JsonDiffPatchPackageVersion>
12+
<JsonDiffPatchPackageVersion>1.3.0</JsonDiffPatchPackageVersion>
1313
<Authors>Wei Chen</Authors>
1414
<PackageProjectUrl>https://github.com/weichch/system-text-json-jsondiffpatch</PackageProjectUrl>
1515
<Copyright>Copyright © Wei Chen 2022</Copyright>

src/SystemTextJson.JsonDiffPatch/Diffs/Formatters/DefaultDeltaFormatter.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ static T CheckType<T>(JsonNode? node)
7676
protected virtual TResult? FormatArray(ref JsonDiffDelta delta, JsonArray left, TResult? existingValue)
7777
{
7878
var arrayChangeEnumerable = _usePatchableArrayChangeEnumerable
79-
? delta.GetPatchableArrayChangeEnumerable(left, false)
79+
? delta.GetPatchableArrayChangeEnumerable(left)
8080
: delta.GetArrayChangeEnumerable();
8181

8282
return arrayChangeEnumerable

src/SystemTextJson.JsonDiffPatch/Diffs/JsonDiffOptions.cs

Lines changed: 9 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -18,35 +18,26 @@ public class JsonDiffOptions
1818
public bool SuppressDetectArrayMove { get; set; }
1919

2020
/// <summary>
21-
/// Gets or sets the function to match array items.
21+
/// Gets or sets custom function to match array items when array items are found not equal using default match
22+
/// algorithm.
2223
/// </summary>
2324
public ArrayItemMatch? ArrayItemMatcher { get; set; }
2425

2526
/// <summary>
26-
/// Gets or sets the function to find key of a <see cref="JsonObject"/>
27-
/// or <see cref="JsonArray"/>. This is used when matching array items by
28-
/// their keys. If this function returns <c>null</c>, the items being
29-
/// compared are treated as "not keyed". When comparing two "not keyed"
30-
/// objects, their contents are compared. This function is only used when
31-
/// <see cref="ArrayItemMatcher"/> is set to <c>null</c>.
27+
/// Gets or sets the function to find key of a <see cref="JsonObject"/> or <see cref="JsonArray"/>.
3228
/// </summary>
3329
public Func<JsonNode?, int, object?>? ArrayObjectItemKeyFinder { get; set; }
3430

3531
/// <summary>
36-
/// Gets or sets whether two instances of JSON object types (object and array)
37-
/// are considered equal if their position is the same in their parent
38-
/// arrays regardless of their contents. This property is only used when
39-
/// <see cref="ArrayItemMatcher"/> is set to <c>null</c>. By settings this
40-
/// property to <c>true</c>, a diff could be returned faster but larger in
41-
/// size. Default value is <c>false</c>.
32+
/// Gets or sets whether two JSON objects (object or array) should be considered equal if they are at the same
33+
/// index inside their parent arrays.
4234
/// </summary>
4335
public bool ArrayObjectItemMatchByPosition { get; set; }
4436

4537
/// <summary>
46-
/// Gets or sets the minimum length for diffing texts using <see cref="TextDiffProvider"/>
47-
/// or default text diffing algorithm, aka Google's diff-match-patch algorithm. When text
48-
/// diffing algorithm is not used, text diffing is fallback to value replacement. If this
49-
/// property is set to <c>0</c>, diffing algorithm is disabled. Default value is <c>0</c>.
38+
/// Gets or sets the minimum length of texts that should be compared using long text comparison algorithm, e.g.
39+
/// <c>diff_match_patch</c> from Google. If this property is set to <c>0</c>, long text comparison algorithm is
40+
/// disabled. Default value is <c>0</c>.
5041
/// </summary>
5142
public int TextDiffMinLength { get; set; }
5243

@@ -68,7 +59,7 @@ public class JsonDiffOptions
6859
internal ref JsonComparerOptions CreateComparerOptions()
6960
{
7061
if (JsonElementComparison != _comparerOptions.JsonElementComparison
71-
|| !Equals(ValueComparer, _comparerOptions.ValueComparer))
62+
|| !ReferenceEquals(ValueComparer, _comparerOptions.ValueComparer))
7263
{
7364
_comparerOptions = new JsonComparerOptions(JsonElementComparison, ValueComparer);
7465
}

src/SystemTextJson.JsonDiffPatch/Diffs/JsonDiffPatcher.Array.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ private static void DiffArray(
2121
return;
2222
}
2323

24-
var comparerOptions = options.CreateComparerOptions();
24+
ref var comparerOptions = ref options.CreateComparerOptions();
2525

2626
// Find command head
2727
int commonHead;

src/SystemTextJson.JsonDiffPatch/Diffs/JsonDiffPatcher.Diff.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,8 @@ private static void DiffInternal(
252252

253253
// None of the above methods returned a result, fallback to check if both values are deeply equal
254254
// This should also handle DateTime and other CLR types that are strings in JSON
255-
if (!left.DeepEquals(right, options.CreateComparerOptions()))
255+
ref var comparerOptions = ref options.CreateComparerOptions();
256+
if (!left.DeepEquals(right, comparerOptions))
256257
{
257258
delta.Modified(left, right);
258259
}

src/SystemTextJson.JsonDiffPatch/Diffs/Lcs.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ public static Lcs Get(Span<JsonNode?> x, Span<JsonNode?> y, JsonDiffOptions opti
100100
// We only cache JSON values as they are more efficient to cache than objects and arrays.
101101
var valueCacheRented = ArrayPool<JsonValueComparisonContext>.Shared.Rent(x.Length + y.Length);
102102
var valueCacheSpan = valueCacheRented.AsSpan(0, x.Length + y.Length);
103-
var comparerOptions = options.CreateComparerOptions();
103+
ref var comparerOptions = ref options.CreateComparerOptions();
104104

105105
matrix.Fill(0);
106106
matchMatrixSpan.Fill(0);

src/SystemTextJson.JsonDiffPatch/Patching/JsonDiffPatcher.Array.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ private static void PatchArray(JsonArray left, JsonObject patch, JsonPatchOption
1010
// When make changes in this method, also copy the changes to ReversePatch* method
1111

1212
var arrayDelta = new JsonDiffDelta(patch);
13-
foreach (var entry in arrayDelta.GetPatchableArrayChangeEnumerable(left, false))
13+
foreach (var entry in arrayDelta.GetPatchableArrayChangeEnumerable(left))
1414
{
1515
var delta = entry.Diff;
1616
var kind = delta.Kind;

src/SystemTextJson.JsonDiffPatch/SystemTextJson.JsonDiffPatch.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
<PropertyGroup>
88
<PackageId>SystemTextJson.JsonDiffPatch</PackageId>
99
<PackageTags>json;diff;compare;patch;system-text-json;jsondiffpatch</PackageTags>
10-
<Description>High-performance, low-allocating JSON objects diff and patch extension for System.Text.Json. Support generating patch document in RFC 6902 JSON Patch format. Provides bonus DeepClone and DeepEquals methods.</Description>
10+
<Description>High-performance, low-allocating JSON object diff and patch extension for System.Text.Json. Support generating patch document in RFC 6902 JSON Patch format. Provides bonus DeepClone and DeepEquals methods.</Description>
1111
</PropertyGroup>
1212

1313
<ItemGroup>

0 commit comments

Comments
 (0)