Commit 6d9694e
authored
Cache parsed object names (#675)
* Cache parsed object names
Looking up the fstring table isn't that cheap, if we assume most object
names are likely to re-appear, it makes sense to keep a small cache of them on
the stack.
e.g. `[{"foo": 1, "bar": 2}, {"foo": 3, "bar": 4}]`
In term of implementation, we use a simple sorted array with binary search
as it's the most compact and guarantee a decent `O(log n)`, and the comparison
is first done on the string length, an then fallback to `memcmp`.
Compactness is important here because we're allocating on the stack.
There is also some simple heuristic to avoid caching names that aren't
likely to be repeated, such as numerical names or very long names.
This helps quite a bit on `activitypub`, `twitter` and `ctim_catalog` macro-benchmarks.
Before:
```
== Parsing activitypub.json (58160 bytes)
ruby 3.3.4 (2024-07-09 revision be1089c8ec) +YJIT [arm64-darwin23]
Warming up --------------------------------------
json 714.000 i/100ms
oj 801.000 i/100ms
Oj::Parser 943.000 i/100ms
rapidjson 632.000 i/100ms
Calculating -------------------------------------
json 7.135k (± 0.7%) i/s (140.16 μs/i) - 35.700k in 5.003978s
oj 7.991k (± 0.2%) i/s (125.14 μs/i) - 40.050k in 5.012044s
Oj::Parser 9.611k (± 0.2%) i/s (104.04 μs/i) - 48.093k in 5.003723s
rapidjson 6.318k (± 0.2%) i/s (158.29 μs/i) - 31.600k in 5.001896s
Comparison:
json: 7134.7 i/s
Oj::Parser: 9611.5 i/s - 1.35x faster
oj: 7990.8 i/s - 1.12x faster
rapidjson: 6317.6 i/s - 1.13x slower
== Parsing twitter.json (567916 bytes)
ruby 3.3.4 (2024-07-09 revision be1089c8ec) +YJIT [arm64-darwin23]
Warming up --------------------------------------
json 57.000 i/100ms
oj 62.000 i/100ms
Oj::Parser 78.000 i/100ms
rapidjson 56.000 i/100ms
Calculating -------------------------------------
json 573.527 (± 1.6%) i/s (1.74 ms/i) - 2.907k in 5.070094s
oj 619.368 (± 1.6%) i/s (1.61 ms/i) - 3.100k in 5.006550s
Oj::Parser 770.095 (± 0.9%) i/s (1.30 ms/i) - 3.900k in 5.064768s
rapidjson 560.601 (± 0.4%) i/s (1.78 ms/i) - 2.856k in 5.094597s
Comparison:
json: 573.5 i/s
Oj::Parser: 770.1 i/s - 1.34x faster
oj: 619.4 i/s - 1.08x faster
rapidjson: 560.6 i/s - 1.02x slower
== Parsing citm_catalog.json (1727030 bytes)
ruby 3.3.4 (2024-07-09 revision be1089c8ec) +YJIT [arm64-darwin23]
Warming up --------------------------------------
json 31.000 i/100ms
oj 34.000 i/100ms
Oj::Parser 46.000 i/100ms
rapidjson 38.000 i/100ms
Calculating -------------------------------------
json 319.842 (± 0.3%) i/s (3.13 ms/i) - 1.612k in 5.040026s
oj 329.315 (± 2.4%) i/s (3.04 ms/i) - 1.666k in 5.061887s
Oj::Parser 452.725 (± 1.1%) i/s (2.21 ms/i) - 2.300k in 5.080996s
rapidjson 358.160 (± 0.8%) i/s (2.79 ms/i) - 1.824k in 5.093054s
Comparison:
json: 319.8 i/s
Oj::Parser: 452.7 i/s - 1.42x faster
rapidjson: 358.2 i/s - 1.12x faster
oj: 329.3 i/s - 1.03x faster
```
After:
```
== Parsing activitypub.json (58160 bytes)
ruby 3.3.4 (2024-07-09 revision be1089c8ec) +YJIT [arm64-darwin23]
Warming up --------------------------------------
json 832.000 i/100ms
oj 799.000 i/100ms
Oj::Parser 969.000 i/100ms
rapidjson 636.000 i/100ms
Calculating -------------------------------------
json 8.020k (± 0.3%) i/s (124.70 μs/i) - 40.768k in 5.083607s
oj 7.942k (± 1.7%) i/s (125.92 μs/i) - 39.950k in 5.031909s
Oj::Parser 9.515k (± 4.4%) i/s (105.10 μs/i) - 47.481k in 5.001202s
rapidjson 6.282k (± 2.1%) i/s (159.20 μs/i) - 31.800k in 5.064719s
Comparison:
json: 8019.6 i/s
Oj::Parser: 9514.5 i/s - 1.19x faster
oj: 7941.9 i/s - same-ish: difference falls within error
rapidjson: 6281.6 i/s - 1.28x slower
== Parsing twitter.json (567916 bytes)
ruby 3.3.4 (2024-07-09 revision be1089c8ec) +YJIT [arm64-darwin23]
Warming up --------------------------------------
json 67.000 i/100ms
oj 62.000 i/100ms
Oj::Parser 79.000 i/100ms
rapidjson 55.000 i/100ms
Calculating -------------------------------------
json 670.935 (± 2.7%) i/s (1.49 ms/i) - 3.417k in 5.096850s
oj 618.937 (± 3.2%) i/s (1.62 ms/i) - 3.100k in 5.014800s
Oj::Parser 768.894 (± 1.7%) i/s (1.30 ms/i) - 3.871k in 5.036093s
rapidjson 556.882 (± 2.7%) i/s (1.80 ms/i) - 2.805k in 5.040970s
Comparison:
json: 670.9 i/s
Oj::Parser: 768.9 i/s - 1.15x faster
oj: 618.9 i/s - 1.08x slower
rapidjson: 556.9 i/s - 1.20x slower
== Parsing citm_catalog.json (1727030 bytes)
ruby 3.3.4 (2024-07-09 revision be1089c8ec) +YJIT [arm64-darwin23]
Warming up --------------------------------------
json 35.000 i/100ms
oj 32.000 i/100ms
Oj::Parser 43.000 i/100ms
rapidjson 39.000 i/100ms
Calculating -------------------------------------
json 382.262 (± 2.6%) i/s (2.62 ms/i) - 1.925k in 5.039336s
oj 348.721 (± 0.6%) i/s (2.87 ms/i) - 1.760k in 5.047265s
Oj::Parser 478.294 (± 0.6%) i/s (2.09 ms/i) - 2.408k in 5.034798s
rapidjson 398.740 (± 0.8%) i/s (2.51 ms/i) - 2.028k in 5.086365s
Comparison:
json: 382.3 i/s
Oj::Parser: 478.3 i/s - 1.25x faster
rapidjson: 398.7 i/s - 1.04x faster
oj: 348.7 i/s - 1.10x slower
```1 parent 217017e commit 6d9694e
0 commit comments