Skip to content

Commit

Permalink
Improve Oj.dump performance
Browse files Browse the repository at this point in the history
This patch uses standard C library to copy the string
because copying one byte at a time is slow.

This patch will improve `Oj.dump` performance as following.

-                | before   | after    | result
--               | --       | --       | --
Oj.dump          | 689.236k | 1.853M   | 2.69x
Oj.dump (compat) | 476.107k | 827.446k | 1.74x
Oj.dump (rails)  | 464.545k | 644.494k | 1.39x

### Environment
- MacBook Air (M1, 2020)
- macOS 12.0 beta 3
- Apple M1
- Ruby 3.0.2

### Before
```
Warming up --------------------------------------
             Oj.dump    69.210k i/100ms
    Oj.dump (compat)    47.123k i/100ms
     Oj.dump (rails)    45.911k i/100ms
Calculating -------------------------------------
             Oj.dump    689.236k (± 0.2%) i/s -      3.460M in   5.020801s
    Oj.dump (compat)    476.107k (± 0.9%) i/s -      2.403M in   5.048128s
     Oj.dump (rails)    464.545k (± 0.9%) i/s -      2.341M in   5.040711s
```

### After
```
Warming up --------------------------------------
             Oj.dump   187.096k i/100ms
    Oj.dump (compat)    82.879k i/100ms
     Oj.dump (rails)    64.371k i/100ms
Calculating -------------------------------------
             Oj.dump      1.853M (± 0.3%) i/s -      9.355M in   5.049406s
    Oj.dump (compat)    827.446k (± 0.2%) i/s -      4.144M in   5.008145s
     Oj.dump (rails)    644.494k (± 0.2%) i/s -      3.283M in   5.093814s
```

### Test code
```ruby
require 'benchmark/ips'
require 'oj'

data = {
  'short_string': 'a' * 50,
  'long_string': 'b' * 255,
  'utf8_string': 'あいうえお' * 10
}

Benchmark.ips do |x|
  x.report('Oj.dump') { Oj.dump(data) }
  x.report('Oj.dump (compat)') { Oj.dump(data, mode: :compat) }
  x.report('Oj.dump (rails)') { Oj.dump(data, mode: :rails) }
end
```
  • Loading branch information
Watson1978 committed Jul 25, 2021
1 parent 55e1e35 commit c37668a
Showing 1 changed file with 8 additions and 9 deletions.
17 changes: 8 additions & 9 deletions ext/oj/dump.c
Expand Up @@ -535,17 +535,17 @@ void oj_dump_xml_time(VALUE obj, Out out) {
}
if ((0 == nsec && !out->opts->sec_prec_set) || 0 == out->opts->sec_prec) {
if (0 == tzsecs && rb_funcall2(obj, oj_utcq_id, 0, 0)) {
sprintf(buf,
int len = sprintf(buf,
"%04d-%02d-%02dT%02d:%02d:%02dZ",
ti.year,
ti.mon,
ti.day,
ti.hour,
ti.min,
ti.sec);
oj_dump_cstr(buf, 20, 0, 0, out);
oj_dump_cstr(buf, len, 0, 0, out);
} else {
sprintf(buf,
int len = sprintf(buf,
"%04d-%02d-%02dT%02d:%02d:%02d%c%02d:%02d",
ti.year,
ti.mon,
Expand All @@ -556,7 +556,7 @@ void oj_dump_xml_time(VALUE obj, Out out) {
tzsign,
tzhour,
tzmin);
oj_dump_cstr(buf, 25, 0, 0, out);
oj_dump_cstr(buf, len, 0, 0, out);
}
} else if (0 == tzsecs && rb_funcall2(obj, oj_utcq_id, 0, 0)) {
char format[64] = "%04d-%02d-%02dT%02d:%02d:%02d.%09ldZ";
Expand All @@ -566,7 +566,7 @@ void oj_dump_xml_time(VALUE obj, Out out) {
format[32] = '0' + out->opts->sec_prec;
len -= 9 - out->opts->sec_prec;
}
sprintf(buf, format, ti.year, ti.mon, ti.day, ti.hour, ti.min, ti.sec, (long)nsec);
len = sprintf(buf, format, ti.year, ti.mon, ti.day, ti.hour, ti.min, ti.sec, (long)nsec);
oj_dump_cstr(buf, len, 0, 0, out);
} else {
char format[64] = "%04d-%02d-%02dT%02d:%02d:%02d.%09ld%c%02d:%02d";
Expand All @@ -576,7 +576,7 @@ void oj_dump_xml_time(VALUE obj, Out out) {
format[32] = '0' + out->opts->sec_prec;
len -= 9 - out->opts->sec_prec;
}
sprintf(buf,
len = sprintf(buf,
format,
ti.year,
ti.mon,
Expand Down Expand Up @@ -827,9 +827,8 @@ void oj_dump_cstr(const char *str, size_t cnt, bool is_sym, bool escape1, Out ou
if (is_sym) {
*out->cur++ = ':';
}
for (; '\0' != *str; str++) {
*out->cur++ = *str;
}
strncpy(out->cur, str, cnt);
out->cur += cnt;
*out->cur++ = '"';
} else {
const char *end = str + cnt;
Expand Down

0 comments on commit c37668a

Please sign in to comment.