Skip to content

Commit ed03e0e

Browse files
authored
Optimize parsing option args in JSON.parse (#720)
This PR will optimize the parsing options in `JSON.parse`. It looked up one by one to make sure that the supported options were included in passed options. This approach of looking up the passed hash each time has an overhead. This PR will apply the same changing as #683 − | before | after | result -- | -- | -- | -- Oj.dump | 518.709k | 528.462k | 1.019x ### Environment - MacBook Pro (M1 Max, 2021) - macOS 12.0 - Apple M1 Max - Ruby 3.0.2 ### Before ``` Warming up -------------------------------------- JSON.parse 51.973k i/100ms Calculating ------------------------------------- JSON.parse 518.709k (± 0.3%) i/s - 5.197M in 10.019799s ``` ### After ``` Warming up -------------------------------------- JSON.parse 52.534k i/100ms Calculating ------------------------------------- JSON.parse 528.462k (± 0.5%) i/s - 5.306M in 10.040606s ``` ### Test code ```ruby require 'benchmark/ips' require 'oj' json =<<-EOF { "$id": "https://example.com/person.schema.json", "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "Person", "type": "object", "properties": { "firstName": { "type": "string", "description": "The person's first name." }, "lastName": { "type": "string", "description": "The person's last name." }, "age": { "description": "Age in years which must be equal to or greater than zero.", "type": "integer", "minimum": 0 } } } EOF Benchmark.ips do |x| x.warmup = 10 x.time = 10 Oj.mimic_JSON x.report('JSON.parse') { JSON.parse(json, symbolize_names: true) } end ```
1 parent a4efd89 commit ed03e0e

File tree

1 file changed

+47
-49
lines changed

1 file changed

+47
-49
lines changed

ext/oj/mimic_json.c

Lines changed: 47 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -499,6 +499,52 @@ oj_mimic_pretty_generate(int argc, VALUE *argv, VALUE self) {
499499
return mimic_generate_core(2, rargs, &copts);
500500
}
501501

502+
static int parse_options_cb(VALUE k, VALUE v, VALUE info) {
503+
struct _parseInfo *pi = (struct _parseInfo *)info;
504+
505+
if (oj_symbolize_names_sym == k) {
506+
pi->options.sym_key = (Qtrue == v) ? Yes : No;
507+
} else if (oj_quirks_mode_sym == k) {
508+
pi->options.quirks_mode = (Qtrue == v) ? Yes : No;
509+
} else if (oj_create_additions_sym == k) {
510+
pi->options.create_ok = (Qtrue == v) ? Yes : No;
511+
} else if (oj_allow_nan_sym == k) {
512+
pi->options.allow_nan = (Qtrue == v) ? Yes : No;
513+
} else if (oj_hash_class_sym == k) {
514+
if (Qnil == v) {
515+
pi->options.hash_class = Qnil;
516+
} else {
517+
rb_check_type(v, T_CLASS);
518+
pi->options.hash_class = v;
519+
}
520+
} else if (oj_object_class_sym == k) {
521+
if (Qnil == v) {
522+
pi->options.hash_class = Qnil;
523+
} else {
524+
rb_check_type(v, T_CLASS);
525+
pi->options.hash_class = v;
526+
}
527+
} else if (oj_array_class_sym == k) {
528+
if (Qnil == v) {
529+
pi->options.array_class = Qnil;
530+
} else {
531+
rb_check_type(v, T_CLASS);
532+
pi->options.array_class = v;
533+
}
534+
} else if (oj_decimal_class_sym == k) {
535+
pi->options.compat_bigdec = (oj_bigdecimal_class == v);
536+
} else if (oj_max_nesting_sym == k) {
537+
if (Qtrue == v) {
538+
pi->max_depth = 100;
539+
} else if (Qfalse == v || Qnil == v) {
540+
pi->max_depth = 0;
541+
} else if (T_FIXNUM == rb_type(v)) {
542+
pi->max_depth = NUM2INT(v);
543+
}
544+
}
545+
return ST_CONTINUE;
546+
}
547+
502548
static VALUE mimic_parse_core(int argc, VALUE *argv, VALUE self, bool bang) {
503549
struct _parseInfo pi;
504550
VALUE ropts;
@@ -524,59 +570,11 @@ static VALUE mimic_parse_core(int argc, VALUE *argv, VALUE self, bool bang) {
524570
pi.max_depth = 100;
525571

526572
if (Qnil != ropts) {
527-
VALUE v;
528-
529573
if (T_HASH != rb_type(ropts)) {
530574
rb_raise(rb_eArgError, "options must be a hash.");
531575
}
532-
if (Qnil != (v = rb_hash_lookup(ropts, oj_symbolize_names_sym))) {
533-
pi.options.sym_key = (Qtrue == v) ? Yes : No;
534-
}
535-
if (Qnil != (v = rb_hash_lookup(ropts, oj_quirks_mode_sym))) {
536-
pi.options.quirks_mode = (Qtrue == v) ? Yes : No;
537-
}
538-
if (Qnil != (v = rb_hash_lookup(ropts, oj_create_additions_sym))) {
539-
pi.options.create_ok = (Qtrue == v) ? Yes : No;
540-
}
541-
if (Qnil != (v = rb_hash_lookup(ropts, oj_allow_nan_sym))) {
542-
pi.options.allow_nan = (Qtrue == v) ? Yes : No;
543-
}
544576

545-
if (oj_hash_has_key(ropts, oj_hash_class_sym)) {
546-
if (Qnil == (v = rb_hash_lookup(ropts, oj_hash_class_sym))) {
547-
pi.options.hash_class = Qnil;
548-
} else {
549-
rb_check_type(v, T_CLASS);
550-
pi.options.hash_class = v;
551-
}
552-
}
553-
if (oj_hash_has_key(ropts, oj_object_class_sym)) {
554-
if (Qnil == (v = rb_hash_lookup(ropts, oj_object_class_sym))) {
555-
pi.options.hash_class = Qnil;
556-
} else {
557-
rb_check_type(v, T_CLASS);
558-
pi.options.hash_class = v;
559-
}
560-
}
561-
if (oj_hash_has_key(ropts, oj_array_class_sym)) {
562-
if (Qnil == (v = rb_hash_lookup(ropts, oj_array_class_sym))) {
563-
pi.options.array_class = Qnil;
564-
} else {
565-
rb_check_type(v, T_CLASS);
566-
pi.options.array_class = v;
567-
}
568-
}
569-
if (oj_hash_has_key(ropts, oj_decimal_class_sym)) {
570-
pi.options.compat_bigdec = (oj_bigdecimal_class == rb_hash_lookup(ropts, oj_decimal_class_sym));
571-
}
572-
v = rb_hash_lookup(ropts, oj_max_nesting_sym);
573-
if (Qtrue == v) {
574-
pi.max_depth = 100;
575-
} else if (Qfalse == v || Qnil == v) {
576-
pi.max_depth = 0;
577-
} else if (T_FIXNUM == rb_type(v)) {
578-
pi.max_depth = NUM2INT(v);
579-
}
577+
rb_hash_foreach(ropts, parse_options_cb, (VALUE)&pi);
580578
oj_parse_opt_match_string(&pi.options.str_rx, ropts);
581579
if (Yes == pi.options.create_ok && Yes == pi.options.sym_key) {
582580
rb_raise(rb_eArgError, ":symbolize_names and :create_additions can not both be true.");

0 commit comments

Comments
 (0)