You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: _posts/2024/2024-04-17-more-performance-updates.md
+17-14Lines changed: 17 additions & 14 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -6,13 +6,13 @@ toc: true
6
6
pin: false
7
7
---
8
8
9
-
I've been focused on performance, specifically memory management, a lot recently. My latest target has been _JsonPointer.Net_.
9
+
I've been focused on performance a lot recently, specifically memory management. My latest target has been _JsonPointer.Net_.
10
10
11
-
I've made a significant update that I hope will make everyone's day a little better. This post explores the architectural differences and the fallout of the changes in the other libs.
11
+
This post explores the architectural differences between the latest update and previous versions, as well as the fallout of changes in the other libs.
12
12
13
13
## Regarding performance
14
14
15
-
Parsing numbers are_way_ down!
15
+
Parsing numbers is_way_ down!
16
16
17
17
This benchmark measures parsing the set of pointers in the spec _n_ times.
18
18
@@ -34,13 +34,13 @@ This benchmark takes those same pointers and just combines them to themselves.
34
34
| Version| n | Mean | Error | StdDev | Gen0 | Allocated |
The run time just about tripled, but the memory usage went down slightly. We'll talk about the reason behind the increase in the next section about the architecture changes.
43
+
The memory usage went down a bit, but the run time is about 50% longer. We'll talk about the reason behind the increase in the next section about the architecture changes.
44
44
45
45
## A new architecture and a new API
46
46
@@ -52,7 +52,7 @@ In v5, `JsonPointer` is a struct that holds the entire pointer as a string along
52
52
53
53
In previous versions, when one pointer needed to be concatenated with another pointer (or any additional segments), the resulting pointer could just take the `PointerSegment` instances it wanted without having to allocate new ones. That means that multiple pointers can actually share `PointerSegment` instances.
54
54
55
-
However, because the new architecture just stores the entire string, it has to basically build a new string and then parse the whole thing to get the new ranges. This explains the longer run time and why the memory improvement isn't as significant.
55
+
However, because the new architecture just stores the entire string, it has to build a new string, which results in a memory allocation.
56
56
57
57
I'm continuing to work on this, and hopefully I'll have updates out soon to address this.
58
58
@@ -66,7 +66,7 @@ To address that you're not getting decoded string segments, I've also defined so
66
66
67
67
-`.SegmentEquals()` - an allocation-free string comparison extension on `ReadOnlySpan<char>` that accounts for JSON Pointer's need to encode the `~` and `/` characters.
68
68
-`.GetSegmentName()` - decodes a segment into a string.
69
-
-`.GetSegmentIndex()` - parses the segment int an int (int segments don't have to worry about encoding though).
69
+
-`.GetSegmentIndex()` - parses the segment into an int (int segments don't have to worry about encoding though).
70
70
71
71
## Fallout
72
72
@@ -79,11 +79,14 @@ But when I updated _JsonSchema.Net_, it seemed a good time to make some other ch
79
79
> You can view and play with the new concept in my [schema/experiment-modelless-schema](https://github.com/gregsdennis/json-everything/tree/schema/experiment-modelless-schema) branch.
80
80
{: .prompt-info }
81
81
82
-
While those updates did result in a few breaking changes, like the previous few major versions, unless you're building your own keywords, it's not likely going to affect you much.
82
+
While those updates did result in a few breaking changes, unless you're building your own keywords, it's not likely going to affect you much, if at all.
83
+
84
+
> You can see what changed in _JsonPatch.Net_ and _JsonSchema.Net_ in [these commits](https://github.com/gregsdennis/json-everything/pull/719/files/98dff44238c6d252e6a0a5b80e2f54c86be70b86#diff-0106bcd119785c478a42e8a021100335a9a6f9c22b0bb2a4da59a47d25aeb400) and the release notes are in the [docs](https://docs.json-everything.net).
85
+
{: .prompt-info }
83
86
84
87
## _JsonSchema.Net_ updates
85
88
86
-
While I can say that the performance noticeably improved, it's not quite as much as I had hoped. I think part of that is the pointer math problem I mentioned before; evaluating schemas _does_ do a lot of pointer math. So if I can figure that out, evaluating schemas will just benefit.
89
+
While I can say that the run times noticeably improved, the reduction in memory usage isn't quite as much as I had hoped. I think part of that is the pointer math problem I mentioned before; evaluating schemas _does_ do a lot of pointer math. So if I can figure that out, evaluating schemas will just benefit.
87
90
88
91
### Performance
89
92
@@ -101,7 +104,7 @@ The improvements are
101
104
- single evaluation - 27% reduced run time / 5% reduced allocations
102
105
- repeated evaluations - 22% reduced run time / negligible allocation reduction
103
106
104
-
I was really hoping for more out of this exercise, but something is... something. And as with JSON Pointer, I'll keep working on it.
107
+
I was really hoping for more out of this exercise, but anything is something. And as with JSON Pointer, I'll keep working on it.
105
108
106
109
### API changes
107
110
@@ -113,7 +116,7 @@ The first is a slight change to `IJsonSchemaKeyword.GetConstraint()`. One of th
113
116
114
117
#### Schema meta-data
115
118
116
-
Previously, I was storing all of the schema meta-data, like anchors, on the schema itself, but in my experiments, I discovered that it made sense to move that stuff to the schema registry. This meant that the registry could perform a lot of stuff at registration time that would have otherwise be done at evaluation time:
119
+
Previously, I was storing all of the schema meta-data, like anchors, on the schema itself, but in my experiments, I discovered that it made sense to move that stuff to the schema registry. This meant that I could pre-calculate a lot at registration time that would have otherwise be done at evaluation time:
117
120
118
121
- scan for anchors (found in `$id`, `$anchor`, `$recursiveAnchor`, and `$dynamicAnchor`)
119
122
- set base URIs
@@ -126,7 +129,7 @@ Since this data is now identified through a one-time static analysis, I don't ha
126
129
127
130
The schema registry follows a "default pattern" where there's a single static instance, `.Global`, but there are also local instances on the evaluation options. Searching the local one will automatically search the global one as a fallback. It's really quite useful for when you want to register the dependent schemas for an evaluation, but you don't want all evaluations to have access to them.
128
131
129
-
I had followed this same pattern with vocabularies as well. However reflecting on it, I think I was over-engineering. The keyword registry is static, and it made sense that the vocabulary registry should also be static.
132
+
I had followed this same pattern with vocabularies as well. However reflecting on it, I think I was over-engineering: mindlessly following a design pattern. The keyword registry is static, and it makes sense that the vocabulary registry should also be static.
0 commit comments