Skip to content

Commit

Permalink
Sort the conditional statements (#946)
Browse files Browse the repository at this point in the history
This patch will sort the conditional statements to reduce the conditional branches
by skipping if statements that do not need to be executed depending on the value of `first`.

−       | before   | after    | result
--      | --       | --       | --
Oj.load | 325.594k | 341.356k | 1.048x

### Environment
- Linux
  - Manjaro Linux x86_64
  - Kernel: 6.12.4-1-MANJARO
  - AMD Ryzen 9 8945HS
  - gcc version 14.2.1
  - Ruby 3.4.1

### Code
```ruby
require 'bundler/inline'
gemfile do
  source 'https://rubygems.org'
  gem 'benchmark-ips'
  gem 'oj'
end

json = '{"a":"Alpha","b":true,"c":12345,"d":[true,[false,[-123456789,null],3.9676,["Something else.",false],null]],"e":{"zero":null,"one":1,"two":2,"three":[3],"four":[0,1,2,3,4]},"f":null,"h":{"a":{"b":{"c":{"d":{"e":{"f":{"g":null}}}}}}},"i":[[[[[[[null]]]]]]]}'

Benchmark.ips do |x|
  x.time = 10
  x.report('Oj.load compat') { Oj.load(json, mode: :compat) }
end
```

### Before
```
$ ruby json_load.rb
ruby 3.4.1 (2024-12-25 revision 48d4efcb85) +PRISM [x86_64-linux]
Warming up --------------------------------------
      Oj.load compat    32.352k i/100ms
Calculating -------------------------------------
      Oj.load compat    325.594k (± 1.8%) i/s    (3.07 μs/i) -      3.268M in  10.039265s
```

### After
```
$ ruby json_load.rb
ruby 3.4.1 (2024-12-25 revision 48d4efcb85) +PRISM [x86_64-linux]
Warming up --------------------------------------
      Oj.load compat    34.274k i/100ms
Calculating -------------------------------------
      Oj.load compat    341.356k (± 3.6%) i/s    (2.93 μs/i) -      3.427M in  10.056686s
```
  • Loading branch information
Watson1978 authored Dec 28, 2024
1 parent b8014b1 commit e14d18d
Showing 1 changed file with 16 additions and 14 deletions.
30 changes: 16 additions & 14 deletions ext/oj/parse.c
Original file line number Diff line number Diff line change
Expand Up @@ -681,26 +681,28 @@ void oj_parse2(ParseInfo pi) {
pi->cur = pi->json;
err_init(&pi->err);
while (1) {
if (0 < pi->max_depth && pi->max_depth <= pi->stack.tail - pi->stack.head - 1) {
if (RB_UNLIKELY(0 < pi->max_depth && pi->max_depth <= pi->stack.tail - pi->stack.head - 1)) {
VALUE err_clas = oj_get_json_err_class("NestingError");

oj_set_error_at(pi, err_clas, __FILE__, __LINE__, "Too deeply nested.");
pi->err_class = err_clas;
return;
}
next_non_white(pi);
if (!first && '\0' != *pi->cur) {
oj_set_error_at(pi,
oj_parse_error_class,
__FILE__,
__LINE__,
"unexpected characters after the JSON document");
}

// If no tokens are consumed (i.e. empty string), throw a parse error
// this is the behavior of JSON.parse in both Ruby and JS.
if (No == pi->options.empty_string && 1 == first && '\0' == *pi->cur) {
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected character");
if (first) {
// If no tokens are consumed (i.e. empty string), throw a parse error
// this is the behavior of JSON.parse in both Ruby and JS.
if (RB_UNLIKELY('\0' == *pi->cur && No == pi->options.empty_string)) {
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected character");
}
} else {
if (RB_UNLIKELY('\0' != *pi->cur)) {
oj_set_error_at(pi,
oj_parse_error_class,
__FILE__,
__LINE__,
"unexpected characters after the JSON document");
}
}

switch (*pi->cur++) {
Expand Down Expand Up @@ -761,7 +763,7 @@ void oj_parse2(ParseInfo pi) {
case '\0': pi->cur--; return;
default: oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected character"); return;
}
if (err_has(&pi->err)) {
if (RB_UNLIKELY(err_has(&pi->err))) {
return;
}
if (stack_empty(&pi->stack)) {
Expand Down

0 comments on commit e14d18d

Please sign in to comment.