| Age | Commit message (Collapse) | Author |
|
Move all the decimal_class option parsing in the constructor.
https://github.com/ruby/json/commit/e9adefdc38
Notes:
Merged: https://github.com/ruby/ruby/pull/12598
|
|
Doesn't make a measurable performance difference but is a
bit clearer.
https://github.com/ruby/json/commit/314d117c61
Notes:
Merged: https://github.com/ruby/ruby/pull/12598
|
|
https://github.com/ruby/json/commit/dd9c46c805
Notes:
Merged: https://github.com/ruby/ruby/pull/12598
|
|
We only use that buffer for parsing integer and floats, these
are unlikely to be very big, and if so we can just use RB_ALLOCV as it will
almost always end in a small `alloca`.
This allow to no longer need `rb_protect` around the parser.
https://github.com/ruby/json/commit/994859916a
Notes:
Merged: https://github.com/ruby/ruby/pull/12598
|
|
https://github.com/ruby/json/commit/591056a526
Notes:
Merged: https://github.com/ruby/ruby/pull/12598
|
|
And get rid of the Ragel parser.
This is 7% faster on activitypub, 15% after on twitter and 11% faster
on citm_catalog.
There might be some more optimization opportunities, I did a quick
optimization pass to fix a regression in string parsing, but other
than that I haven't dug much in performance.
Notes:
Merged: https://github.com/ruby/ruby/pull/12598
|
|
Ref: https://github.com/ruby/json/pull/728
Ref: https://github.com/ruby/ruby/pull/12569
https://github.com/ruby/json/commit/30a4a86954
|
|
Ref: https://github.com/ruby/json/pull/718
The existing `Parser` interface is pretty bad, as it forces to
instantiate a new instance for each document.
Instead it's preferable to only take the config and do all the
initialization needed, and then keep the parsing state on the
stack on in ephemeral memory.
This refactor makes the `JSON::Coder` pull request much easier to
implement in a performant way.
https://github.com/ruby/json/commit/c8d5236a92
Co-Authored-By: Étienne Barrié <etienne.barrie@gmail.com>
|
|
Co-authored-by: Jean Boussier <jean.boussier@gmail.com>
Notes:
Merged: https://github.com/ruby/ruby/pull/12568
|
|
Introduce a simplified table for the most common case, which is
`script_safe: false, ascii_only: false`.
On the `script_safe` table, now only `0xE2` does a multi-byte check.
Merge back `convert_ASCII_to_JSON`, as it no longer help much with
the simplified escape table.
```
== Encoding mixed utf8 (5003001 bytes)
ruby 3.4.1 (2024-12-25 revision 48d4efcb85) +YJIT +PRISM [arm64-darwin23]
Warming up --------------------------------------
after 38.000 i/100ms
Calculating -------------------------------------
after 398.220 (± 3.0%) i/s (2.51 ms/i) - 2.014k in 5.061659s
Comparison:
before: 381.8 i/s
after: 398.2 i/s - same-ish: difference falls within error
== Encoding mostly utf8 (5001001 bytes)
ruby 3.4.1 (2024-12-25 revision 48d4efcb85) +YJIT +PRISM [arm64-darwin23]
Warming up --------------------------------------
after 39.000 i/100ms
Calculating -------------------------------------
after 393.337 (± 2.5%) i/s (2.54 ms/i) - 1.989k in 5.059397s
Comparison:
before: 304.3 i/s
after: 393.3 i/s - 1.29x faster
== Encoding twitter.json (466906 bytes)
ruby 3.4.1 (2024-12-25 revision 48d4efcb85) +YJIT +PRISM [arm64-darwin23]
Warming up --------------------------------------
after 244.000 i/100ms
Calculating -------------------------------------
after 2.436k (± 0.9%) i/s (410.43 μs/i) - 12.200k in 5.007702s
Comparison:
before: 2125.9 i/s
after: 2436.5 i/s - 1.15x faster
```
|
|
https://github.com/ruby/json/commit/f745ec145e
Notes:
Merged: https://github.com/ruby/ruby/pull/12394
|
|
Check for existence of strnlen() and use alternative code if it is missing.
https://github.com/ruby/json/commit/48d4bbc3a0
Notes:
Merged: https://github.com/ruby/ruby/pull/12394
|
|
https://github.com/ruby/json/commit/e1f6456499
Notes:
Merged: https://github.com/ruby/ruby/pull/12267
|
|
Fix: https://github.com/ruby/json/issues/715
The first byte check was missing.
https://github.com/ruby/json/commit/93a7f8717d
Notes:
Merged: https://github.com/ruby/ruby/pull/12267
|
|
GCC 13 prints the following warning.
https://rubyci.s3.amazonaws.com/ubuntu/ruby-master/log/20241127T001003Z.log.html.gz
```
compiling generator.c
generator.c: In function ‘raise_generator_error’:
generator.c:91:5: warning: function ‘raise_generator_error’ might be a candidate for ‘gnu_printf’ format attribute [-Wsuggest-attribute=format]
91 | VALUE str = rb_vsprintf(fmt, args);
| ^~~~~
```
This change prevents the warning by specifying the format attribute.
https://github.com/ruby/json/commit/b8c1490846
|
|
Fix: https://github.com/ruby/json/issues/710
Makes it easier to debug why a given tree of objects can't
be dumped as JSON.
Co-Authored-By: Étienne Barrié <etienne.barrie@gmail.com>
|
|
It's using `rb_gc_mark_maybe` under the hood, which isn't what
we need.
https://github.com/ruby/json/commit/e10d0bffcd
|
|
Ref: https://github.com/ruby/json/issues/524
Rather than to buffer everything in memory.
Unfortunately Ruby doesn't provide an API to write into
and IO without first allocating a string, which is a bit
wasteful.
https://github.com/ruby/json/commit/f017af6c0a
|
|
https://github.com/ruby/json/commit/2d62ec449f
Notes:
Merged: https://github.com/ruby/ruby/pull/12103
|
|
https://github.com/ruby/json/commit/d5e4a6e3fd
Notes:
Merged: https://github.com/ruby/ruby/pull/12103
|
|
https://github.com/ruby/json/commit/61f022dfbd
Notes:
Merged: https://github.com/ruby/ruby/pull/12103
|
|
Otherwise the likeliness of seeing that key again is really low, and looking up
the cache is just a waste.
Before:
```
== Parsing small hash (65 bytes)
ruby 3.4.0dev (2024-11-13T12:32:57Z fstr-update-callba.. https://github.com/ruby/json/commit/9b44b455b3) +YJIT +PRISM [arm64-darwin24]
Warming up --------------------------------------
json 343.049k i/100ms
oj 213.943k i/100ms
Oj::Parser 31.583k i/100ms
rapidjson 303.433k i/100ms
Calculating -------------------------------------
json 3.704M (± 1.5%) i/s (270.01 ns/i) - 18.525M in 5.003078s
oj 2.200M (± 1.1%) i/s (454.46 ns/i) - 11.125M in 5.056526s
Oj::Parser 285.369k (± 4.8%) i/s (3.50 μs/i) - 1.453M in 5.103866s
rapidjson 3.216M (± 1.6%) i/s (310.95 ns/i) - 16.082M in 5.001973s
Comparison:
json: 3703517.4 i/s
rapidjson: 3215983.0 i/s - 1.15x slower
oj: 2200417.1 i/s - 1.68x slower
Oj::Parser: 285369.1 i/s - 12.98x slower
== Parsing test from oj (258 bytes)
ruby 3.4.0dev (2024-11-13T12:32:57Z fstr-update-callba.. https://github.com/ruby/json/commit/9b44b455b3) +YJIT +PRISM [arm64-darwin24]
Warming up --------------------------------------
json 54.539k i/100ms
oj 41.473k i/100ms
Oj::Parser 24.064k i/100ms
rapidjson 51.466k i/100ms
Calculating -------------------------------------
json 549.386k (± 1.6%) i/s (1.82 μs/i) - 2.781M in 5.064316s
oj 417.003k (± 1.3%) i/s (2.40 μs/i) - 2.115M in 5.073047s
Oj::Parser 226.500k (± 4.7%) i/s (4.42 μs/i) - 1.131M in 5.005466s
rapidjson 526.124k (± 1.0%) i/s (1.90 μs/i) - 2.676M in 5.087176s
Comparison:
json: 549385.6 i/s
rapidjson: 526124.3 i/s - 1.04x slower
oj: 417003.4 i/s - 1.32x slower
Oj::Parser: 226500.4 i/s - 2.43x slower
```
After:
```
== Parsing small hash (65 bytes)
ruby 3.4.0dev (2024-11-13T12:32:57Z fstr-update-callba.. https://github.com/ruby/json/commit/9b44b455b3) +YJIT +PRISM [arm64-darwin24]
Warming up --------------------------------------
json 361.394k i/100ms
oj 217.203k i/100ms
Oj::Parser 28.855k i/100ms
rapidjson 303.404k i/100ms
Calculating -------------------------------------
json 3.859M (± 2.9%) i/s (259.13 ns/i) - 19.515M in 5.061302s
oj 2.191M (± 1.6%) i/s (456.49 ns/i) - 11.077M in 5.058043s
Oj::Parser 315.132k (± 7.1%) i/s (3.17 μs/i) - 1.587M in 5.065707s
rapidjson 3.156M (± 4.0%) i/s (316.88 ns/i) - 15.777M in 5.008949s
Comparison:
json: 3859046.5 i/s
rapidjson: 3155778.5 i/s - 1.22x slower
oj: 2190616.0 i/s - 1.76x slower
Oj::Parser: 315132.4 i/s - 12.25x slower
== Parsing test from oj (258 bytes)
ruby 3.4.0dev (2024-11-13T12:32:57Z fstr-update-callba.. https://github.com/ruby/json/commit/9b44b455b3) +YJIT +PRISM [arm64-darwin24]
Warming up --------------------------------------
json 55.682k i/100ms
oj 40.343k i/100ms
Oj::Parser 25.119k i/100ms
rapidjson 51.500k i/100ms
Calculating -------------------------------------
json 555.808k (± 1.4%) i/s (1.80 μs/i) - 2.784M in 5.010092s
oj 412.283k (± 1.7%) i/s (2.43 μs/i) - 2.098M in 5.089900s
Oj::Parser 279.306k (±13.3%) i/s (3.58 μs/i) - 1.356M in 5.022079s
rapidjson 517.177k (± 2.7%) i/s (1.93 μs/i) - 2.626M in 5.082352s
Comparison:
json: 555808.3 i/s
rapidjson: 517177.1 i/s - 1.07x slower
oj: 412283.2 i/s - 1.35x slower
Oj::Parser: 279306.5 i/s - 1.99x slower
```
https://github.com/ruby/json/commit/00c45ddc9f
|
|
Fix: https://github.com/ruby/json/issues/697
This way even if `Encoding.default_external` is set to a weird value
the document will be parsed just fine.
https://github.com/ruby/json/commit/3a8505a8fa
|
|
https://github.com/ruby/json/commit/e51e796697
|
|
Before this commit, we would try to scan for a float, then if that
failed, scan for an integer. But floats and integers have many bytes in
common, so we would end up scanning the same bytes multiple times.
This patch combines integer and float scanning machines so that we only
have to scan bytes once. If the machine finds "float parts", then it
executes the "isFloat" transition in the machine, which sets a boolean
letting us know that the parser found a float.
If we didn't find a float, but we did match, then we know it's an int.
https://github.com/ruby/json/commit/0c0e0930cd
|
|
https://github.com/ruby/json/commit/49650f7312
|
|
Fix: https://github.com/ruby/json/issues/694
This was lost during the .gemspec merge and not noticed
because it was falling back to loading the jars from the
stdlib.
https://github.com/ruby/json/commit/07cf261251
|
|
https://github.com/ruby/json/commit/8071cc6f09
|
|
`rb_cstr2inum` isn't very fast because it handles tons of
different scenarios, and also require a NULL terminated string
which forces us to copy the number into a secondary buffer.
But since the parser already computed the length, we can much more
cheaply do this with a very simple function as long as the number
is small enough to fit into a native type (`long long`).
If the number is too long, we can fallback to the `rb_cstr2inum`
slowpath.
Before:
```
== Parsing citm_catalog.json (1727030 bytes)
ruby 3.4.0dev (2024-11-06T07:59:09Z precompute-hash-wh.. https://github.com/ruby/json/commit/7943f98a8a) +YJIT +PRISM [arm64-darwin24]
Warming up --------------------------------------
json 40.000 i/100ms
oj 35.000 i/100ms
Oj::Parser 45.000 i/100ms
rapidjson 38.000 i/100ms
Calculating -------------------------------------
json 425.941 (± 1.9%) i/s (2.35 ms/i) - 2.160k in 5.072833s
oj 349.617 (± 1.7%) i/s (2.86 ms/i) - 1.750k in 5.006953s
Oj::Parser 464.767 (± 1.7%) i/s (2.15 ms/i) - 2.340k in 5.036381s
rapidjson 382.413 (± 2.4%) i/s (2.61 ms/i) - 1.938k in 5.070757s
Comparison:
json: 425.9 i/s
Oj::Parser: 464.8 i/s - 1.09x faster
rapidjson: 382.4 i/s - 1.11x slower
oj: 349.6 i/s - 1.22x slower
```
After:
```
== Parsing citm_catalog.json (1727030 bytes)
ruby 3.4.0dev (2024-11-06T07:59:09Z precompute-hash-wh.. https://github.com/ruby/json/commit/7943f98a8a) +YJIT +PRISM [arm64-darwin24]
Warming up --------------------------------------
json 46.000 i/100ms
oj 33.000 i/100ms
Oj::Parser 45.000 i/100ms
rapidjson 39.000 i/100ms
Calculating -------------------------------------
json 462.332 (± 3.2%) i/s (2.16 ms/i) - 2.346k in 5.080504s
oj 351.140 (± 1.1%) i/s (2.85 ms/i) - 1.782k in 5.075616s
Oj::Parser 473.500 (± 1.3%) i/s (2.11 ms/i) - 2.385k in 5.037695s
rapidjson 395.052 (± 3.5%) i/s (2.53 ms/i) - 1.989k in 5.042275s
Comparison:
json: 462.3 i/s
Oj::Parser: 473.5 i/s - same-ish: difference falls within error
rapidjson: 395.1 i/s - 1.17x slower
oj: 351.1 i/s - 1.32x slower
```
https://github.com/ruby/json/commit/3a4dc9e1b4
|
|
Ignoring `CHAR_BITS` > 8 platform, as far as `ch` indexes
`escape_table` that is hard-coded as 256 elements.
```
../../../../src/ext/json/generator/generator.c(121): warning C4333: '>>': right shift by too large amount, data loss
../../../../src/ext/json/generator/generator.c(122): warning C4333: '>>': right shift by too large amount, data loss
../../../../src/ext/json/generator/generator.c(243): warning C4333: '>>': right shift by too large amount, data loss
../../../../src/ext/json/generator/generator.c(244): warning C4333: '>>': right shift by too large amount, data loss
../../../../src/ext/json/generator/generator.c(291): warning C4333: '>>': right shift by too large amount, data loss
../../../../src/ext/json/generator/generator.c(292): warning C4333: '>>': right shift by too large amount, data loss
```
https://github.com/ruby/json/commit/fb82373612
|
|
If we assume most string don't contain any escape sequence we can avoid
a lot of costly operations when it holds true.
Before:
```
== Parsing activitypub.json (58160 bytes)
ruby 3.4.0dev (2024-11-06T07:59:09Z precompute-hash-wh.. https://github.com/ruby/json/commit/7943f98a8a) +YJIT +PRISM [arm64-darwin24]
Warming up --------------------------------------
json 884.000 i/100ms
oj 789.000 i/100ms
Oj::Parser 943.000 i/100ms
rapidjson 584.000 i/100ms
Calculating -------------------------------------
json 8.897k (± 1.3%) i/s (112.40 μs/i) - 45.084k in 5.068520s
oj 7.967k (± 1.5%) i/s (125.52 μs/i) - 40.239k in 5.051985s
Oj::Parser 9.564k (± 1.4%) i/s (104.56 μs/i) - 48.093k in 5.029626s
rapidjson 5.947k (± 1.4%) i/s (168.16 μs/i) - 29.784k in 5.009437s
Comparison:
json: 8896.5 i/s
Oj::Parser: 9563.8 i/s - 1.08x faster
oj: 7966.8 i/s - 1.12x slower
rapidjson: 5946.7 i/s - 1.50x slower
== Parsing twitter.json (567916 bytes)
ruby 3.4.0dev (2024-11-06T07:59:09Z precompute-hash-wh.. https://github.com/ruby/json/commit/7943f98a8a) +YJIT +PRISM [arm64-darwin24]
Warming up --------------------------------------
json 83.000 i/100ms
oj 64.000 i/100ms
Oj::Parser 77.000 i/100ms
rapidjson 54.000 i/100ms
Calculating -------------------------------------
json 823.083 (± 1.8%) i/s (1.21 ms/i) - 4.150k in 5.043805s
oj 632.538 (± 1.4%) i/s (1.58 ms/i) - 3.200k in 5.060073s
Oj::Parser 769.122 (± 1.8%) i/s (1.30 ms/i) - 3.850k in 5.007501s
rapidjson 548.494 (± 1.5%) i/s (1.82 ms/i) - 2.754k in 5.022153s
Comparison:
json: 823.1 i/s
Oj::Parser: 769.1 i/s - 1.07x slower
oj: 632.5 i/s - 1.30x slower
rapidjson: 548.5 i/s - 1.50x slower
== Parsing citm_catalog.json (1727030 bytes)
ruby 3.4.0dev (2024-11-06T07:59:09Z precompute-hash-wh.. https://github.com/ruby/json/commit/7943f98a8a) +YJIT +PRISM [arm64-darwin24]
Warming up --------------------------------------
json 41.000 i/100ms
oj 34.000 i/100ms
Oj::Parser 45.000 i/100ms
rapidjson 39.000 i/100ms
Calculating -------------------------------------
json 427.162 (± 1.2%) i/s (2.34 ms/i) - 2.173k in 5.087666s
oj 351.463 (± 2.8%) i/s (2.85 ms/i) - 1.768k in 5.035149s
Oj::Parser 461.849 (± 3.7%) i/s (2.17 ms/i) - 2.340k in 5.074461s
rapidjson 395.155 (± 1.8%) i/s (2.53 ms/i) - 1.989k in 5.034927s
Comparison:
json: 427.2 i/s
Oj::Parser: 461.8 i/s - 1.08x faster
rapidjson: 395.2 i/s - 1.08x slower
oj: 351.5 i/s - 1.22x slower
```
After:
```
== Parsing activitypub.json (58160 bytes)
ruby 3.4.0dev (2024-11-06T07:59:09Z precompute-hash-wh.. https://github.com/ruby/json/commit/7943f98a8a) +YJIT +PRISM [arm64-darwin24]
Warming up --------------------------------------
json 953.000 i/100ms
oj 813.000 i/100ms
Oj::Parser 956.000 i/100ms
rapidjson 563.000 i/100ms
Calculating -------------------------------------
json 9.525k (± 1.2%) i/s (104.98 μs/i) - 47.650k in 5.003252s
oj 8.117k (± 0.5%) i/s (123.20 μs/i) - 40.650k in 5.008283s
Oj::Parser 9.590k (± 3.2%) i/s (104.27 μs/i) - 48.756k in 5.089794s
rapidjson 6.020k (± 0.9%) i/s (166.10 μs/i) - 30.402k in 5.050155s
Comparison:
json: 9525.3 i/s
Oj::Parser: 9590.1 i/s - same-ish: difference falls within error
oj: 8116.7 i/s - 1.17x slower
rapidjson: 6020.5 i/s - 1.58x slower
== Parsing twitter.json (567916 bytes)
ruby 3.4.0dev (2024-11-06T07:59:09Z precompute-hash-wh.. https://github.com/ruby/json/commit/7943f98a8a) +YJIT +PRISM [arm64-darwin24]
Warming up --------------------------------------
json 87.000 i/100ms
oj 64.000 i/100ms
Oj::Parser 75.000 i/100ms
rapidjson 55.000 i/100ms
Calculating -------------------------------------
json 866.563 (± 0.8%) i/s (1.15 ms/i) - 4.350k in 5.020138s
oj 643.567 (± 0.8%) i/s (1.55 ms/i) - 3.264k in 5.072101s
Oj::Parser 777.346 (± 3.5%) i/s (1.29 ms/i) - 3.900k in 5.023933s
rapidjson 557.158 (± 0.7%) i/s (1.79 ms/i) - 2.805k in 5.034731s
Comparison:
json: 866.6 i/s
Oj::Parser: 777.3 i/s - 1.11x slower
oj: 643.6 i/s - 1.35x slower
rapidjson: 557.2 i/s - 1.56x slower
== Parsing citm_catalog.json (1727030 bytes)
ruby 3.4.0dev (2024-11-06T07:59:09Z precompute-hash-wh.. https://github.com/ruby/json/commit/7943f98a8a) +YJIT +PRISM [arm64-darwin24]
Warming up --------------------------------------
json 41.000 i/100ms
oj 35.000 i/100ms
Oj::Parser 40.000 i/100ms
rapidjson 39.000 i/100ms
Calculating -------------------------------------
json 429.216 (± 1.2%) i/s (2.33 ms/i) - 2.173k in 5.063351s
oj 354.755 (± 1.1%) i/s (2.82 ms/i) - 1.785k in 5.032374s
Oj::Parser 465.114 (± 3.7%) i/s (2.15 ms/i) - 2.360k in 5.081634s
rapidjson 387.135 (± 1.3%) i/s (2.58 ms/i) - 1.950k in 5.037787s
Comparison:
json: 429.2 i/s
Oj::Parser: 465.1 i/s - 1.08x faster
rapidjson: 387.1 i/s - 1.11x slower
oj: 354.8 i/s - 1.21x slower
```
https://github.com/ruby/json/commit/96bd97c61e
|
|
https://github.com/ruby/json/commit/1acce7aceb
|
|
Notes:
Merged: https://github.com/ruby/ruby/pull/12003
|
|
Notes:
Merged: https://github.com/ruby/ruby/pull/12003
|
|
subclasses
Ref: https://github.com/ruby/json/pull/674
Ref: https://github.com/ruby/json/pull/668
The behavior on such case it quite unclear, the goal here is to
figure out whatever was the behavior on Cext version of `json 2.7.0`
and get all implementations to converge.
We can then decide to make them all behave differently if we so wish.
https://github.com/ruby/json/commit/614921dcef
Notes:
Merged: https://github.com/ruby/ruby/pull/12003
|
|
https://github.com/ruby/json/commit/90c8aaaa6a
Notes:
Merged: https://github.com/ruby/ruby/pull/12003
|
|
This is somewhat dead code as unless you are using `JSON::Parser.new`
direcltly we never allocate `JSON::Ext::Parser` anymore.
But still, we should mark all its reference in case some code out there
uses that.
Followup: #675
https://github.com/ruby/json/commit/8bf74a977b
Notes:
Merged: https://github.com/ruby/ruby/pull/12003
|
|
Rather than to copy into a buffer to unescape and then copy that
buffer into the final string, we can directly copy into the final
string.
The downside is that if the string contains a lot of escaping, we
end up returning a string that's larger than strictly necessary, but
it's probably fine.
Before:
```
== Parsing twitter.json (567916 bytes)
ruby 3.3.4 (2024-07-09 revision https://github.com/ruby/json/commit/be1089c8ec) +YJIT [arm64-darwin23]
Warming up --------------------------------------
json 56.000 i/100ms
oj 58.000 i/100ms
oj strict 74.000 i/100ms
Oj::Parser 76.000 i/100ms
rapidjson 52.000 i/100ms
Calculating -------------------------------------
json 556.659 (± 2.9%) i/s (1.80 ms/i) - 2.800k in 5.034719s
oj 604.077 (± 3.8%) i/s (1.66 ms/i) - 3.016k in 5.001546s
oj strict 706.942 (± 3.5%) i/s (1.41 ms/i) - 3.552k in 5.030954s
Oj::Parser 752.917 (± 3.2%) i/s (1.33 ms/i) - 3.800k in 5.052707s
rapidjson 546.470 (± 3.5%) i/s (1.83 ms/i) - 2.756k in 5.049855s
Comparison:
json: 556.7 i/s
Oj::Parser: 752.9 i/s - 1.35x faster
oj strict: 706.9 i/s - 1.27x faster
oj: 604.1 i/s - 1.09x faster
rapidjson: 546.5 i/s - same-ish: difference falls within error
== Parsing citm_catalog.json (1727030 bytes)
ruby 3.3.4 (2024-07-09 revision https://github.com/ruby/json/commit/be1089c8ec) +YJIT [arm64-darwin23]
Warming up --------------------------------------
json 29.000 i/100ms
oj 32.000 i/100ms
oj strict 38.000 i/100ms
Oj::Parser 42.000 i/100ms
rapidjson 38.000 i/100ms
Calculating -------------------------------------
json 317.858 (± 3.1%) i/s (3.15 ms/i) - 1.595k in 5.023245s
oj 348.168 (± 2.6%) i/s (2.87 ms/i) - 1.760k in 5.058431s
oj strict 394.599 (± 2.8%) i/s (2.53 ms/i) - 1.976k in 5.012073s
Oj::Parser 403.771 (± 3.0%) i/s (2.48 ms/i) - 2.058k in 5.101578s
rapidjson 383.441 (± 3.7%) i/s (2.61 ms/i) - 1.938k in 5.061355s
Comparison:
json: 317.9 i/s
Oj::Parser: 403.8 i/s - 1.27x faster
oj strict: 394.6 i/s - 1.24x faster
rapidjson: 383.4 i/s - 1.21x faster
oj: 348.2 i/s - 1.10x faster
```
After:
```
== Parsing twitter.json (567916 bytes)
ruby 3.3.4 (2024-07-09 revision https://github.com/ruby/json/commit/be1089c8ec) +YJIT [arm64-darwin23]
Warming up --------------------------------------
json 56.000 i/100ms
oj 62.000 i/100ms
oj strict 73.000 i/100ms
Oj::Parser 76.000 i/100ms
rapidjson 54.000 i/100ms
Calculating -------------------------------------
json 561.009 (± 7.5%) i/s (1.78 ms/i) - 2.800k in 5.039548s
oj 601.124 (± 4.3%) i/s (1.66 ms/i) - 3.038k in 5.064686s
oj strict 707.455 (± 3.4%) i/s (1.41 ms/i) - 3.577k in 5.062540s
Oj::Parser 751.799 (± 3.1%) i/s (1.33 ms/i) - 3.800k in 5.059509s
rapidjson 535.641 (± 3.2%) i/s (1.87 ms/i) - 2.700k in 5.045816s
Comparison:
json: 561.0 i/s
Oj::Parser: 751.8 i/s - 1.34x faster
oj strict: 707.5 i/s - 1.26x faster
oj: 601.1 i/s - same-ish: difference falls within error
rapidjson: 535.6 i/s - same-ish: difference falls within error
== Parsing citm_catalog.json (1727030 bytes)
ruby 3.3.4 (2024-07-09 revision https://github.com/ruby/json/commit/be1089c8ec) +YJIT [arm64-darwin23]
Warming up --------------------------------------
json 30.000 i/100ms
oj 32.000 i/100ms
oj strict 36.000 i/100ms
Oj::Parser 42.000 i/100ms
rapidjson 39.000 i/100ms
Calculating -------------------------------------
json 313.248 (± 7.3%) i/s (3.19 ms/i) - 1.560k in 5.014118s
oj 341.977 (± 4.1%) i/s (2.92 ms/i) - 1.728k in 5.063332s
oj strict 387.062 (± 6.2%) i/s (2.58 ms/i) - 1.944k in 5.045961s
Oj::Parser 400.423 (± 4.0%) i/s (2.50 ms/i) - 2.016k in 5.044513s
rapidjson 379.046 (± 6.1%) i/s (2.64 ms/i) - 1.911k in 5.064461s
Comparison:
json: 313.2 i/s
Oj::Parser: 400.4 i/s - 1.28x faster
oj strict: 387.1 i/s - 1.24x faster
rapidjson: 379.0 i/s - 1.21x faster
oj: 342.0 i/s - same-ish: difference falls within error
```
https://github.com/ruby/json/commit/5e1ec4a268
|
|
Followup: https://github.com/ruby/json/commit/6382c231b0b8
https://github.com/ruby/json/commit/0c797b4a11
|
|
Fix: https://github.com/ruby/json/issues/667
This is yet another behavior on which the various implementations
differed, but the C implementation used to call `to_json` on String
subclasses used as keys.
This was optimized out in e125072130229e54a651f7b11d7d5a782ae7fb65
but there is an Active Support test case for it, so it's best to
make all 3 implementation respect this behavior.
|
|
Now that older rubies have been droped, we no longer need to check
for all that.
https://github.com/ruby/json/commit/35cf2b84e0
|
|
If that assumption holds true, then we don't need to copy the
string into a buffer to unescape it. For small string is just saves
copying, but for large ones it also saves a malloc/free combo.
Before:
```
== Parsing twitter.json (567916 bytes)
ruby 3.3.4 (2024-07-09 revision https://github.com/ruby/json/commit/be1089c8ec) +YJIT [arm64-darwin23]
Warming up --------------------------------------
json 52.000 i/100ms
oj 61.000 i/100ms
oj strict 70.000 i/100ms
Oj::Parser 71.000 i/100ms
rapidjson 55.000 i/100ms
Calculating -------------------------------------
json 510.111 (± 2.9%) i/s (1.96 ms/i) - 2.548k in 5.000029s
oj 610.232 (± 3.1%) i/s (1.64 ms/i) - 3.050k in 5.003725s
oj strict 713.231 (± 3.2%) i/s (1.40 ms/i) - 3.570k in 5.010902s
Oj::Parser 762.598 (± 3.0%) i/s (1.31 ms/i) - 3.834k in 5.033130s
rapidjson 553.029 (± 7.4%) i/s (1.81 ms/i) - 2.750k in 5.022630s
Comparison:
json: 510.1 i/s
Oj::Parser: 762.6 i/s - 1.49x faster
oj strict: 713.2 i/s - 1.40x faster
oj: 610.2 i/s - 1.20x faster
rapidjson: 553.0 i/s - same-ish: difference falls within error
== Parsing citm_catalog.json (1727030 bytes)
ruby 3.3.4 (2024-07-09 revision https://github.com/ruby/json/commit/be1089c8ec) +YJIT [arm64-darwin23]
Warming up --------------------------------------
json 28.000 i/100ms
oj 33.000 i/100ms
oj strict 37.000 i/100ms
Oj::Parser 43.000 i/100ms
rapidjson 38.000 i/100ms
Calculating -------------------------------------
json 303.853 (± 3.6%) i/s (3.29 ms/i) - 1.540k in 5.076079s
oj 348.009 (± 2.0%) i/s (2.87 ms/i) - 1.749k in 5.027738s
oj strict 396.679 (± 3.3%) i/s (2.52 ms/i) - 1.998k in 5.042271s
Oj::Parser 406.699 (± 2.2%) i/s (2.46 ms/i) - 2.064k in 5.077587s
rapidjson 393.463 (± 3.3%) i/s (2.54 ms/i) - 1.976k in 5.028501s
Comparison:
json: 303.9 i/s
Oj::Parser: 406.7 i/s - 1.34x faster
oj strict: 396.7 i/s - 1.31x faster
rapidjson: 393.5 i/s - 1.29x faster
oj: 348.0 i/s - 1.15x faster
```
After:
```
== Parsing twitter.json (567916 bytes)
ruby 3.3.4 (2024-07-09 revision https://github.com/ruby/json/commit/be1089c8ec) +YJIT [arm64-darwin23]
Warming up --------------------------------------
json 56.000 i/100ms
oj 62.000 i/100ms
oj strict 72.000 i/100ms
Oj::Parser 77.000 i/100ms
rapidjson 55.000 i/100ms
Calculating -------------------------------------
json 568.025 (± 2.1%) i/s (1.76 ms/i) - 2.856k in 5.030272s
oj 630.936 (± 1.4%) i/s (1.58 ms/i) - 3.162k in 5.012630s
oj strict 705.784 (±11.2%) i/s (1.42 ms/i) - 3.456k in 5.006706s
Oj::Parser 783.989 (± 1.7%) i/s (1.28 ms/i) - 3.927k in 5.010343s
rapidjson 557.630 (± 2.0%) i/s (1.79 ms/i) - 2.805k in 5.032388s
Comparison:
json: 568.0 i/s
Oj::Parser: 784.0 i/s - 1.38x faster
oj strict: 705.8 i/s - 1.24x faster
oj: 630.9 i/s - 1.11x faster
rapidjson: 557.6 i/s - same-ish: difference falls within error
== Parsing citm_catalog.json (1727030 bytes)
ruby 3.3.4 (2024-07-09 revision https://github.com/ruby/json/commit/be1089c8ec) +YJIT [arm64-darwin23]
Warming up --------------------------------------
json 29.000 i/100ms
oj 33.000 i/100ms
oj strict 38.000 i/100ms
Oj::Parser 43.000 i/100ms
rapidjson 37.000 i/100ms
Calculating -------------------------------------
json 319.271 (± 3.1%) i/s (3.13 ms/i) - 1.595k in 5.001128s
oj 347.946 (± 1.7%) i/s (2.87 ms/i) - 1.749k in 5.028395s
oj strict 396.914 (± 3.0%) i/s (2.52 ms/i) - 2.014k in 5.079645s
Oj::Parser 409.311 (± 2.7%) i/s (2.44 ms/i) - 2.064k in 5.046626s
rapidjson 394.752 (± 1.5%) i/s (2.53 ms/i) - 1.998k in 5.062776s
Comparison:
json: 319.3 i/s
Oj::Parser: 409.3 i/s - 1.28x faster
oj strict: 396.9 i/s - 1.24x faster
rapidjson: 394.8 i/s - 1.24x faster
oj: 347.9 i/s - 1.09x faster
```
https://github.com/ruby/json/commit/7e0f66546a
|
|
https://github.com/ruby/json/commit/7e557ee291
|
|
* rb_str_conv_enc() returns the source string unmodified
if the conversion did not work. But we should be consistent with
the generator here and only accept BINARY or convertible to UTF-8.
https://github.com/ruby/json/commit/1344ad6f66
|
|
Because of it's Ruby 1.8 heritage, the C extension doesn't care
much about strings encoding. We should get stricter over time.
https://github.com/ruby/json/commit/42402fc13f
|
|
[Feature #19528]
Ref: https://bugs.ruby-lang.org/issues/19528
`load` is understood as the default method for serializer kind of libraries, and
the default options of `JSON.load` has caused many security vulnerabilities over the
years.
The plan is to do like YAML/Psych, deprecate these default options and direct
users toward using `JSON.unsafe_load` so at least it's obvious it should be
used against untrusted data.
|
|
Fix: https://github.com/ruby/json/issues/655
For very small documents, the biggest performance gap with alternatives is
that the API impose that we allocate the `State` object. In a real world app
this doesn't make much of a difference, but when running in a micro-benchmark
this doubles the allocations, causing twice the amount of GC runs, making us
look bad.
However, unless we have to call a `to_json` method, the `State` object isn't
visible, so with some refactoring, we can elude that allocation entirely.
Instead we allocate the State internal struct on the stack, and if we need
to call a `to_json` method, we allocate the `State` and spill the struct on
the heap.
As a result, `JSON.generate` is now as fast as re-using a `State` instance,
as long as only primitives are generated.
Before:
```
== Encoding small mixed (34 bytes)
ruby 3.3.4 (2024-07-09 revision be1089c8ec) +YJIT [arm64-darwin23]
Warming up --------------------------------------
json (reuse) 598.654k i/100ms
json 400.542k i/100ms
oj 533.353k i/100ms
Calculating -------------------------------------
json (reuse) 6.371M (± 8.6%) i/s (156.96 ns/i) - 31.729M in 5.059195s
json 4.120M (± 6.6%) i/s (242.72 ns/i) - 20.828M in 5.090549s
oj 5.622M (± 6.4%) i/s (177.86 ns/i) - 28.268M in 5.061473s
Comparison:
json (reuse): 6371126.6 i/s
oj: 5622452.0 i/s - same-ish: difference falls within error
json: 4119991.1 i/s - 1.55x slower
== Encoding small nested array (121 bytes)
ruby 3.3.4 (2024-07-09 revision be1089c8ec) +YJIT [arm64-darwin23]
Warming up --------------------------------------
json (reuse) 248.125k i/100ms
json 215.255k i/100ms
oj 217.531k i/100ms
Calculating -------------------------------------
json (reuse) 2.628M (± 6.1%) i/s (380.55 ns/i) - 13.151M in 5.030281s
json 2.185M (± 6.7%) i/s (457.74 ns/i) - 10.978M in 5.057655s
oj 2.217M (± 6.7%) i/s (451.10 ns/i) - 11.094M in 5.044844s
Comparison:
json (reuse): 2627799.4 i/s
oj: 2216824.8 i/s - 1.19x slower
json: 2184669.5 i/s - 1.20x slower
== Encoding small hash (65 bytes)
ruby 3.3.4 (2024-07-09 revision be1089c8ec) +YJIT [arm64-darwin23]
Warming up --------------------------------------
json (reuse) 641.334k i/100ms
json 322.745k i/100ms
oj 642.450k i/100ms
Calculating -------------------------------------
json (reuse) 7.133M (± 6.5%) i/s (140.19 ns/i) - 35.915M in 5.068201s
json 4.615M (± 7.0%) i/s (216.70 ns/i) - 22.915M in 5.003718s
oj 6.912M (± 6.4%) i/s (144.68 ns/i) - 34.692M in 5.047690s
Comparison:
json (reuse): 7133123.3 i/s
oj: 6911977.1 i/s - same-ish: difference falls within error
json: 4614696.6 i/s - 1.55x slower
```
After:
```
== Encoding small mixed (34 bytes)
ruby 3.3.4 (2024-07-09 revision be1089c8ec) +YJIT [arm64-darwin23]
Warming up --------------------------------------
json (reuse) 572.751k i/100ms
json 457.741k i/100ms
oj 512.247k i/100ms
Calculating -------------------------------------
json (reuse) 6.324M (± 6.9%) i/s (158.12 ns/i) - 31.501M in 5.023093s
json 6.263M (± 6.9%) i/s (159.66 ns/i) - 31.126M in 5.017086s
oj 5.569M (± 6.6%) i/s (179.56 ns/i) - 27.661M in 5.003739s
Comparison:
json (reuse): 6324183.5 i/s
json: 6263204.9 i/s - same-ish: difference falls within error
oj: 5569049.2 i/s - same-ish: difference falls within error
== Encoding small nested array (121 bytes)
ruby 3.3.4 (2024-07-09 revision be1089c8ec) +YJIT [arm64-darwin23]
Warming up --------------------------------------
json (reuse) 258.505k i/100ms
json 242.335k i/100ms
oj 220.678k i/100ms
Calculating -------------------------------------
json (reuse) 2.589M (± 9.6%) i/s (386.17 ns/i) - 12.925M in 5.071853s
json 2.594M (± 6.6%) i/s (385.46 ns/i) - 13.086M in 5.083035s
oj 2.250M (± 2.3%) i/s (444.43 ns/i) - 11.255M in 5.004707s
Comparison:
json (reuse): 2589499.6 i/s
json: 2594321.0 i/s - same-ish: difference falls within error
oj: 2250064.0 i/s - 1.15x slower
== Encoding small hash (65 bytes)
ruby 3.3.4 (2024-07-09 revision be1089c8ec) +YJIT [arm64-darwin23]
Warming up --------------------------------------
json (reuse) 656.373k i/100ms
json 644.135k i/100ms
oj 650.283k i/100ms
Calculating -------------------------------------
json (reuse) 7.202M (± 7.1%) i/s (138.84 ns/i) - 36.101M in 5.051438s
json 7.278M (± 1.7%) i/s (137.40 ns/i) - 36.716M in 5.046300s
oj 7.036M (± 1.7%) i/s (142.12 ns/i) - 35.766M in 5.084729s
Comparison:
json (reuse): 7202447.9 i/s
json: 7277883.0 i/s - same-ish: difference falls within error
oj: 7036115.2 i/s - same-ish: difference falls within error
```
|
|
While less nice, this open the door to eluding the State object
allocation when possible.
https://github.com/ruby/json/commit/5c0d428d4c
|
|
Given we expect these to almost always be null, we might as
well keep them in RString.
And even when provided, assuming we're passed frozen strings
we'll save on copying them.
This also reduce the size of the struct from 112B to 72B.
https://github.com/ruby/json/commit/6382c231b0
|
|
With the extra logic added for stack allocation, and especially the
memcpy, it became harder for compilers to inline.
This doesn't fully reclaim the speed lost with the stack allocation,
but it's getting closer.
Before:
```
== Encoding twitter.json (466906 bytes)
ruby 3.3.4 (2024-07-09 revision https://github.com/ruby/json/commit/be1089c8ec) +YJIT [arm64-darwin23]
Warming up --------------------------------------
json 160.000 i/100ms
oj 225.000 i/100ms
Calculating -------------------------------------
json 1.577k (± 2.0%) i/s (634.20 μs/i) - 8.000k in 5.075561s
oj 2.264k (± 2.3%) i/s (441.79 μs/i) - 11.475k in 5.072205s
Comparison:
json: 1576.8 i/s
oj: 2263.5 i/s - 1.44x faster
== Encoding citm_catalog.json (500298 bytes)
ruby 3.3.4 (2024-07-09 revision https://github.com/ruby/json/commit/be1089c8ec) +YJIT [arm64-darwin23]
Warming up --------------------------------------
json 101.000 i/100ms
oj 123.000 i/100ms
Calculating -------------------------------------
json 1.033k (± 2.6%) i/s (968.06 μs/i) - 5.252k in 5.087617s
oj 1.257k (± 2.2%) i/s (795.54 μs/i) - 6.396k in 5.090830s
Comparison:
json: 1033.0 i/s
oj: 1257.0 i/s - 1.22x faster
```
After:
```
== Encoding twitter.json (466906 bytes)
ruby 3.3.4 (2024-07-09 revision https://github.com/ruby/json/commit/be1089c8ec) [arm64-darwin23]
Warming up --------------------------------------
json 213.000 i/100ms
oj 230.000 i/100ms
Calculating -------------------------------------
json 2.064k (± 3.6%) i/s (484.44 μs/i) - 10.437k in 5.063685s
oj 2.246k (± 0.7%) i/s (445.19 μs/i) - 11.270k in 5.017541s
Comparison:
json: 2064.2 i/s
oj: 2246.2 i/s - 1.09x faster
== Encoding citm_catalog.json (500298 bytes)
ruby 3.3.4 (2024-07-09 revision https://github.com/ruby/json/commit/be1089c8ec) [arm64-darwin23]
Warming up --------------------------------------
json 133.000 i/100ms
oj 132.000 i/100ms
Calculating -------------------------------------
json 1.327k (± 1.7%) i/s (753.69 μs/i) - 6.650k in 5.013565s
oj 1.305k (± 2.2%) i/s (766.40 μs/i) - 6.600k in 5.061089s
Comparison:
json: 1326.8 i/s
oj: 1304.8 i/s - same-ish: difference falls within error
```
https://github.com/ruby/json/commit/89f816e868
|