[ruby/json] Optimize fbuffer_append_long

Ref: https://github.com/ruby/json/issues/655

Rather than to write the number backward, and then reverse
the buffer, we can start from the back of the buffer and write
the number in the proper direction.

Before:

```
== Encoding integers (8009 bytes)
ruby 3.3.4 (2024-07-09 revision be1089c8ec) +YJIT [arm64-darwin23]
Warming up --------------------------------------
                json     8.606k i/100ms
                  oj     9.598k i/100ms
Calculating -------------------------------------
                json     86.059k (± 0.8%) i/s   (11.62 μs/i) -    430.300k in   5.000416s
                  oj     97.409k (± 0.6%) i/s   (10.27 μs/i) -    489.498k in   5.025360s

Comparison:
                json:    86058.8 i/s
                  oj:    97408.8 i/s - 1.13x  faster
```

After:

```
== Encoding integers (8009 bytes)
ruby 3.3.4 (2024-07-09 revision be1089c8ec) +YJIT [arm64-darwin23]
Warming up --------------------------------------
        json (reuse)     9.500k i/100ms
                json     9.359k i/100ms
                  oj     9.722k i/100ms
Calculating -------------------------------------
        json (reuse)     96.270k (± 0.4%) i/s   (10.39 μs/i) -    484.500k in   5.032777s
                json     94.800k (± 2.2%) i/s   (10.55 μs/i) -    477.309k in   5.037495s
                  oj     97.131k (± 0.7%) i/s   (10.30 μs/i) -    486.100k in   5.004822s

Comparison:
        json (reuse):    96270.1 i/s
                  oj:    97130.5 i/s - same-ish: difference falls within error
                json:    94799.9 i/s - same-ish: difference falls within error
```

0655b58d14
This commit is contained in:
Jean Boussier 2024-10-28 21:08:26 +01:00 committed by Hiroshi SHIBATA
parent b094ee3f23
commit 2e43621806
No known key found for this signature in database
GPG key ID: F9CF13417264FAC2
2 changed files with 10 additions and 17 deletions

View file

@ -64,6 +64,7 @@ benchmark_encoding "small nested array", [[1,2,3,4,5]]*10
benchmark_encoding "small hash", { "username" => "jhawthorn", "id" => 123, "event" => "wrote json serializer" }
# On these benchmarks we perform well. Either on par or very closely faster/slower
benchmark_encoding "integers", (1_000_000..1_001_000).to_a, except: %i(json_state)
benchmark_encoding "mixed utf8", ([("a" * 5000) + "" + ("a" * 5000)] * 500), except: %i(json_state)
benchmark_encoding "mostly utf8", ([("" * 3333)] * 500), except: %i(json_state)
benchmark_encoding "twitter.json", JSON.load_file("#{__dir__}/data/twitter.json"), except: %i(json_state)

View file

@ -107,33 +107,25 @@ static void fbuffer_append_char(FBuffer *fb, char newchr)
}
#ifdef JSON_GENERATOR
static void freverse(char *start, char *end)
{
char c;
while (end > start) {
c = *end, *end-- = *start, *start++ = c;
}
}
static long fltoa(long number, char *buf)
{
static char digits[] = "0123456789";
static const char digits[] = "0123456789";
long sign = number;
char* tmp = buf;
if (sign < 0) number = -number;
do *tmp++ = digits[number % 10]; while (number /= 10);
if (sign < 0) *tmp++ = '-';
freverse(buf, tmp - 1);
return tmp - buf;
do *tmp-- = digits[number % 10]; while (number /= 10);
if (sign < 0) *tmp-- = '-';
return buf - tmp;
}
#define LONG_BUFFER_SIZE 20
static void fbuffer_append_long(FBuffer *fb, long number)
{
char buf[20];
unsigned long len = fltoa(number, buf);
fbuffer_append(fb, buf, len);
char buf[LONG_BUFFER_SIZE];
char *buffer_end = buf + LONG_BUFFER_SIZE;
long len = fltoa(number, buffer_end - 1);
fbuffer_append(fb, buffer_end - len, len);
}
static VALUE fbuffer_to_s(FBuffer *fb)