Skip to content

Commit d92b510

Browse files
committed
edits
1 parent 70a676a commit d92b510

File tree

2 files changed

+17
-14
lines changed

2 files changed

+17
-14
lines changed

.jekyll-metadata

2.58 KB
Binary file not shown.

_posts/2024/2024-04-17-more-performance-updates.md

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,13 @@ toc: true
66
pin: false
77
---
88

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_.
1010

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.
1212

1313
## Regarding performance
1414

15-
Parsing numbers are _way_ down!
15+
Parsing numbers is _way_ down!
1616

1717
This benchmark measures parsing the set of pointers in the spec _n_ times.
1818

@@ -34,13 +34,13 @@ This benchmark takes those same pointers and just combines them to themselves.
3434
| Version| n | Mean | Error | StdDev | Gen0 | Allocated |
3535
|------- |------ |------------:|------------:|------------:|---------:|----------:|
3636
| v4.0.1 | 1 | 661.2 ns | 12.86 ns | 11.40 ns | 1.1473 | 2.34 KB |
37-
| v5.0.0 | 1 | 1.912 us | 0.0376 us | 0.0586 us | 1.1101 | 2.27 KB |
37+
| v5.0.0 | 1 | 916.3 ns | 17.46 ns | 15.47 ns | 1.1120 | 2.27 KB |
3838
| v4.0.1 | 10 | 6,426.4 ns | 124.10 ns | 121.88 ns | 11.4746 | 23.44 KB |
39-
| v5.0.0 | 10 | 18.830 us | 0.3746 us | 0.4600 us | 11.1084 | 22.73 KB |
39+
| v5.0.0 | 10 | 9,128.2 ns | 180.82 ns | 241.39 ns | 11.1237 | 22.73 KB |
4040
| v4.0.1 | 100 | 64,469.6 ns | 1,309.01 ns | 1,093.08 ns | 114.7461 | 234.38 KB |
41-
| v5.0.0 | 100 | 188.406 us | 3.6606 us | 5.1317 us | 111.3281 | 227.34 KB |
41+
| v5.0.0 | 100 | 92,437.0 ns | 1,766.38 ns | 1,963.33 ns | 111.3281 | 227.34 KB |
4242

43-
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.
4444

4545
## A new architecture and a new API
4646

@@ -52,7 +52,7 @@ In v5, `JsonPointer` is a struct that holds the entire pointer as a string along
5252

5353
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.
5454

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.
5656

5757
I'm continuing to work on this, and hopefully I'll have updates out soon to address this.
5858

@@ -66,7 +66,7 @@ To address that you're not getting decoded string segments, I've also defined so
6666

6767
- `.SegmentEquals()` - an allocation-free string comparison extension on `ReadOnlySpan<char>` that accounts for JSON Pointer's need to encode the `~` and `/` characters.
6868
- `.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).
7070

7171
## Fallout
7272

@@ -79,11 +79,14 @@ But when I updated _JsonSchema.Net_, it seemed a good time to make some other ch
7979
> 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.
8080
{: .prompt-info }
8181

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 }
8386

8487
## _JsonSchema.Net_ updates
8588

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.
8790

8891
### Performance
8992

@@ -101,7 +104,7 @@ The improvements are
101104
- single evaluation - 27% reduced run time / 5% reduced allocations
102105
- repeated evaluations - 22% reduced run time / negligible allocation reduction
103106

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.
105108

106109
### API changes
107110

@@ -113,7 +116,7 @@ The first is a slight change to `IJsonSchemaKeyword.GetConstraint()`. One of th
113116

114117
#### Schema meta-data
115118

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:
117120

118121
- scan for anchors (found in `$id`, `$anchor`, `$recursiveAnchor`, and `$dynamicAnchor`)
119122
- set base URIs
@@ -126,7 +129,7 @@ Since this data is now identified through a one-time static analysis, I don't ha
126129

127130
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.
128131

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.
130133

131134
So now it is.
132135

0 commit comments

Comments
 (0)